/*
* FaceCat的Flutter框架
* 上海卷卷猫信息技术有限公司
*/

import 'package:flutter/material.dart';
import 'dart:math';
import 'dart:ui' as ui;
import 'package:xml/xml.dart' as xml; 

const Color noneColor = Color.fromARGB(0, 0, 0, 0);

/*
* 坐标结构
*/
class FCPoint {
  double x = 0; //横坐标
  double y = 0; //纵坐标
  bool isNotEmpty = false; //是否不为空
  FCPoint(double ix, double iy) {
    x = ix;
    y = iy;
    isNotEmpty = true;
  }
}

/*
* 大小结构
*/
class FCSize {
  double cx = 0; //长
  double cy = 0; //宽
  bool isNotEmpty = false; //是否不为空
  FCSize(double icx, double icy) {
    cx = icx;
    cy = icy;
    isNotEmpty = true;
  }
}

/*
* 矩形结构
*/
class FCRect {
  double left = 0; //左侧
  double top = 0; //上侧
  double right = 0; //右侧
  double bottom = 0; //底部
  bool isNotEmpty = false; //是否不为空
  FCRect(double ileft, double itop, double iright, double ibottom) {
    left = ileft;
    top = itop;
    right = iright;
    bottom = ibottom;
    isNotEmpty = true;
  }
}

/*
* 边距信息
*/
class FCPadding {
  double left = 0; //左侧
  double top = 0; //上侧
  double right = 0; //右侧
  double bottom = 0; //底部
  bool isNotEmpty = false; //是否不为空
  FCPadding(double ileft, double itop, double iright, double ibottom) {
    left = ileft;
    top = itop;
    right = iright;
    bottom = ibottom;
    isNotEmpty = true;
  }
}

/*
* 基础视图
*/
class FCView {
  String align = "left"; //横向布局
  bool allowDrag = false; //是否允许拖动
  bool allowResize = false; //是否可以拖动改变大小
  bool allowDragScroll = false; //是否允许拖动滚动
  bool allowPreviewsEvent = false; //是否允许预处理事件
  String backColor = "rgb(255,255,255)"; //背景色
  String backImage = ""; //背景图片
  String borderColor = "rgb(150,150,150)"; //边线色
  double borderWidth = 1; //边线宽度
  FCRect clipRect = FCRect(0, 0, 0, 0); //裁剪区域
  double cornerRadius = 0; //圆角
  bool displayOffset = true; //是否显示偏移量
  String dock = ""; //悬浮状态
  bool downScrollHButton = false; //是否按下横向滚动条
  bool downScrollVButton = false; //是否按下纵向滚动条
  bool enabled = true; //是否可用
  Map<String, String> exAttributes = {}; //额外的属性
  String font = "Default,14"; //字体
  String hoveredColor = "none"; //鼠标悬停时的颜色
  bool hoverScrollHButton = false; //是否悬停横向滚动条
  bool hoverScrollVButton = false; //是否悬停纵向滚动条
  bool hScrollIsVisible = false; //横向滚动是否显示
  bool isNotEmpty = false; //是否不为空
  FCPoint location = FCPoint(0, 0); //坐标
  FCPadding margin = FCPadding(0, 0, 0, 0); //外边距
  FCSize maximumSize = FCSize(0, 0); //大小
  FCPadding padding = FCPadding(0, 0, 0, 0); //内边距
  List<FCPaint> paint = <FCPaint>[]; //绘图对象
  List<FCView> parent = <FCView>[]; //父视图
  String pushedColor = "rgb(200,200,200)"; //鼠标按下时的颜色
  int resizePoint = -1; //调整尺寸的点
  double scrollV = 0; //纵向滚动
  double scrollH = 0; //横向滚动
  double scrollSize = 8; //滚动条的大小
  bool showHScrollBar = false; //是否显示横向滚动条
  bool showVScrollBar = false; //是否显示横向滚动条
  String scrollBarColor = "rgb(200,200,200)"; //滚动条的颜色
  String scrollBarHoveredColor = "rgb(42,138,195)"; //滚动条悬停的颜色
  FCSize size = FCSize(100, 20); //大小
  double startScrollH = 0; //开始滚动的值
  double startScrollV = 0; //结束滚动的值
  FCPoint startPoint = FCPoint(0, 0); //起始点
  FCRect startRect = FCRect(0, 0, 0, 0); //移动开始时的视图矩形
  int tabIndex = 0; //Tab索引
  bool tabStop = false; //是否支持Tab
  String tag = ""; //数据
  String text = ""; //文字
  String textColor = "rgb(0,0,0)"; //前景色
  bool topMost = false; //是否置顶
  int touchDownTime = 0; //鼠标按下的时间
  String viewName = ""; //名称
  String verticalAlign = "top"; //纵向布局
  String viewType = ""; //类型
  bool vScrollIsVisible = false; //纵向滚动是否显示
  List<FCView> views = <FCView>[]; //子视图
  bool visible = true; //可见性
  FCView() {
    isNotEmpty = true;
  }
  FCView getParent() {
    if (parent.isNotEmpty) {
      return parent[0];
    } else {
      FCView noneView = FCView();
      noneView.isNotEmpty = false;
      return noneView;
    }
  }

  void setParent(FCView view) {
    parent = <FCView>[];
    parent.add(view);
  }

  FCPaint getPaint() {
    if (paint.isNotEmpty) {
      return paint[0];
    } else {
      FCPaint nonePaint = FCPaint();
      nonePaint.isNotEmpty = false;
      return nonePaint;
    }
  }

  void setPaint(FCPaint pValue) {
    paint = <FCPaint>[];
    paint.add(pValue);
  }
}

/*
* 按钮
*/
class FCButton extends FCView {
  FCButton() {
    viewType = "button";
  }
}

/*
* 标签
*/
class FCLabel extends FCView {
  FCLabel() {
    viewType = "label";
  }
}

/*
* 标签
*/
class FCTextBox extends FCView {
  FCTextBox() {
    viewType = "textbox";
  }
}

/*
* 图层
*/
class FCDiv extends FCView {
  bool isScrolling = false; //是否正在加速
  int scrollAddSpeed = 0; //滚动加速
  int scrollDirection = 0; //滚动方向
  FCDiv() {
    viewType = "div";
    allowDragScroll = true;
  }
}

/*
* 页
*/
class FCTabPage extends FCView {
  List<FCView> headerButton = <FCView>[]; //页头的按钮
  FCView getHeaderButton() {
    if (headerButton.isNotEmpty) {
      return headerButton[0];
    } else {
      FCView noneView = FCView();
      noneView.isNotEmpty = false;
      return noneView;
    }
  }

  void setHeaderButton(FCView view) {
    headerButton = <FCView>[];
    headerButton.add(view);
  }

  FCTabPage() {
    visible = false;
    viewType = "tabpage";
  }
}

/*
* 多页夹
*/
class FCTabView extends FCView {
  int animationSpeed = 20; //动画速度
  String layout = "top"; //布局方式
  List<FCTabPage> tabPages = <FCTabPage>[]; //页夹集合
  String underLineColor = "none"; //下划线的颜色
  double underLineSize = 0; //下划线的宽度
  FCPoint underPoint = FCPoint(0, 0); //下划点
  bool useAnimation = false; //是否使用动画
  FCTabView() {
    viewType = "tabview";
  }
}

/*
* 复选按钮
*/
class FCCheckBox extends FCView {
  FCSize buttonSize = FCSize(16, 16); //按钮的大小
  bool checked = false; //是否选中
  FCCheckBox() {
    viewType = "checkbox";
  }
}

/*
* 单选按钮
*/
class FCRadioButton extends FCView {
  FCSize buttonSize = FCSize(16, 16); //按钮的大小
  bool checked = false; //是否选中
  String groupName = ""; //组别
  FCRadioButton() {
    viewType = "radiobutton";
  }
}

/*
 * 多布局图层
 */
class FCLayoutDiv extends FCDiv {
  bool autoWrap = false; //是否自动换行
  String layoutStyle = "lefttoright"; //分割方式
  FCLayoutDiv() {
    viewType = "layout";
  }
}

/*
 * 分割图层
 */
class FCSplitLayoutDiv extends FCView {
  List<FCView> firstView = <FCView>[]; //第一个视图
  String layoutStyle = "lefttoright"; //分割方式
  FCSize oldSize = FCSize(0, 0); //上次的尺寸
  List<FCView> secondView = <FCView>[]; //第二个视图
  List<FCView> splitter = <FCView>[]; //分割线
  String splitMode = "absolutesize"; //分割模式 percentsize百分比 或absolutesize绝对值
  double splitPercent = -1; //分割百分比;
  FCView getFirstView() {
    if (firstView.isNotEmpty) {
      return firstView[0];
    } else {
      FCView noneView = FCView();
      noneView.isNotEmpty = false;
      return noneView;
    }
  }

  void setFirstView(FCView view) {
    firstView = <FCView>[];
    firstView.add(view);
  }

  FCView getSecondView() {
    if (secondView.isNotEmpty) {
      return secondView[0];
    } else {
      FCView noneView = FCView();
      noneView.isNotEmpty = false;
      return noneView;
    }
  }

  void setSecondView(FCView view) {
    secondView = <FCView>[];
    secondView.add(view);
  }

  FCView getSplitter() {
    if (splitter.isNotEmpty) {
      return splitter[0];
    } else {
      FCView noneView = FCView();
      noneView.isNotEmpty = false;
      return noneView;
    }
  }

  void setSplitter(FCView view) {
    splitter = <FCView>[];
    splitter.add(view);
  }

  FCSplitLayoutDiv() {
    viewType = "split";
  }
}

/*
* 表格列
*/
class FCGridColumn {
  bool allowSort = true; //是否允许排序
  bool allowResize = false; //是否允许改变大小
  String backColor = "rgb(200,200,200)"; //背景色
  String borderColor = "rgb(150,150,150)"; //边线颜色
  FCRect bounds = FCRect(0, 0, 0, 0); //区域
  String cellAlign = "left"; //left:居左 center:居中 right:居右
  String colName = ""; //名称
  String colType = ""; //类型 string:字符串 double:浮点型 int:整型 bool:布尔型
  String font = "Default,14"; //字体
  bool frozen = false; //是否冻结
  int index = -1; //索引
  bool isNotEmpty = false;
  String sort = "none"; //排序模式
  String text = ""; //文字
  String textColor = "rgb(50,50,50)"; //文字颜色
  bool visible = true; //是否可见
  double width = 120; //宽度
  String widthStr = ""; //宽度字符串
  FCGridColumn() {
    isNotEmpty = true;
  }
}

/*
* 表格单元格
*/
class FCGridCell {
  String backColor = "rgb(255,255,255)"; //背景色
  String borderColor = "rgb(150,150,150)"; //边线色
  int colSpan = 1; //列距
  List<FCGridColumn> column = <FCGridColumn>[]; //所在列
  int digit = -1; //保留小数的位数
  String font = "Default,14"; //字体
  bool isNotEmpty = false;
  List<FCGridRow> row = <FCGridRow>[]; //所在行
  int rowSpan = 1; //行距
  String textColor = "rgb(0,0,0)"; //前景色
  String value = ""; //值
  List<FCView> view = <FCView>[]; //包含的视图
  FCGridCell() {
    isNotEmpty = true;
  }
  FCGridColumn getColumn() {
    if (column.isNotEmpty) {
      return column[0];
    } else {
      FCGridColumn noneColumn = FCGridColumn();
      noneColumn.isNotEmpty = false;
      return noneColumn;
    }
  }

  void setColumn(FCGridColumn col) {
    column = <FCGridColumn>[];
    column.add(col);
  }

  FCGridRow getRow() {
    if (row.isNotEmpty) {
      return row[0];
    } else {
      FCGridRow noneRow = FCGridRow();
      noneRow.isNotEmpty = false;
      return noneRow;
    }
  }
  void setRow(FCGridRow r) {
    row = <FCGridRow>[];
    row.add(r);
  }
  FCView getView() {
    if (view.isNotEmpty) {
      return view[0];
    } else {
      FCView noneView = FCView();
      noneView.isNotEmpty = false;
      return noneView;
    }
  }
  void setView(FCView v) {
    view = <FCView>[];
    view.add(v);
  }
}

/*
* 表格行
*/
class FCGridRow {
  List<FCGridCell> cells = <FCGridCell>[]; //单元格
  int index = -1; //索引
  bool isNotEmpty = false;
  String key = ""; //排序键值
  bool selected = false; //是否选中
  bool visible = true; //是否可见
  FCGridRow() {
    isNotEmpty = true;
  }
}

/*
* 表格
*/
class FCGrid extends FCView {
  List<FCGridColumn> columns = <FCGridColumn>[]; //所有的列
  double headerHeight = 30; //头部高度
  bool isScrolling = false; //是否正在加速
  double rowHeight = 30; //行高
  List<FCGridRow> rows = <FCGridRow>[]; //包含的所有行
  String selectedRowColor = "rgb(125,125,125)"; //选中行的颜色
  int scrollAddSpeed = 0; //滚动加速
  int scrollDirection = 0; //滚动方向
  FCGrid() {
    viewType = "grid";
    allowDragScroll = true;
    showHScrollBar = true;
    showVScrollBar = true;
  }
}

/*
* 树的列
*/
class FCTreeColumn {
  FCRect bounds = FCRect(0, 0, 0, 0); //区域
  int index = -1; //索引
  bool isNotEmpty = false;
  bool visible = true; //是否可见
  double width = 120; //宽度
  String widthStr = ""; //宽度字符串
  FCTreeColumn() {
    isNotEmpty = true;
  }
}

/*
* 树的行
*/
class FCTreeRow {
  List<FCTreeNode> cells = <FCTreeNode>[]; //单元格
  int index = -1; //索引
  bool isNotEmpty = false;
  bool selected = false; //是否选中
  bool visible = true; //是否可见
  FCTreeRow() {
    isNotEmpty = true;
  }
}

/*
 * 树节点
 */
class FCTreeNode {
  bool allowCollapsed = true; //是否允许折叠
  String backColor = "rgb(255,255,255)"; //背景色
  bool checked = false; //是否选中
  List<FCTreeNode> childNodes = <FCTreeNode>[]; //子节点
  bool collapsed = false; //是否折叠
  List<FCTreeColumn> column = <FCTreeColumn>[]; //所有的列
  String font = "Default,14"; //字体
  double indent = 0; //缩进
  bool isNotEmpty = false;
  List<FCTreeNode> parentNode = <FCTreeNode>[];
  List<FCTreeRow> row = <FCTreeRow>[]; //包含的所有行
  String textColor = "rgb(0,0,0)"; //前景色
  String value = ""; //值
  FCTreeNode() {
    isNotEmpty = true;
  }
  FCTreeColumn getColumn() {
    if (column.isNotEmpty) {
      return column[0];
    } else {
      FCTreeColumn noneColumn = FCTreeColumn();
      noneColumn.isNotEmpty = false;
      return noneColumn;
    }
  }

  void setColumn(FCTreeColumn col) {
    column = <FCTreeColumn>[];
    column.add(col);
  }

  FCTreeRow getRow() {
    if (row.isNotEmpty) {
      return row[0];
    } else {
      FCTreeRow noneRow = FCTreeRow();
      noneRow.isNotEmpty = false;
      return noneRow;
    }
  }

  void setRow(FCTreeRow r) {
    row = <FCTreeRow>[];
    row.add(r);
  }

  FCTreeNode getParentNode() {
    if (parentNode.isNotEmpty) {
      return parentNode[0];
    } else {
      FCTreeNode noneNode = FCTreeNode();
      noneNode.isNotEmpty = false;
      return noneNode;
    }
  }

  void setParentNode(FCTreeNode node) {
    parentNode = <FCTreeNode>[];
    parentNode.add(node);
  }
}

/*
* 树
*/
class FCTree extends FCView {
  double checkBoxWidth = 25; //复选框占用的宽度
  List<FCTreeNode> childNodes = <FCTreeNode>[]; //子节点
  double collapsedWidth = 25; //折叠按钮占用的宽度
  List<FCTreeColumn> columns = <FCTreeColumn>[]; //所有的列
  double headerHeight = 0; //头部高度
  double indent = 20; //缩进
  bool isScrolling = false; //是否正在加速
  double rowHeight = 30; //行高
  List<FCTreeRow> rows = <FCTreeRow>[]; //包含的所有行
  int scrollAddSpeed = 0; //滚动加速
  int scrollDirection = 0; //滚动方向
  bool showCheckBox = false; //是否显示复选框
  FCTree() {
    viewType = "tree";
    allowDragScroll = true;
    showHScrollBar = true;
    showVScrollBar = true;
  }
}

/*
* 证券数据结构
*/
class SecurityData {
  double amount = 0; //成交额
  double close = 0; //收盘价
  double date = 0; //日期，为1970年到现在的秒
  double high = 0; //最高价
  double low = 0; //最低价
  double open = 0; //开盘价
  double volume = 0; //成交额
}

/*
* 基础图形
*/
class BaseShape {
  String color = "none"; //颜色
  String color2 = "none"; //颜色2
  List<double> datas = <double>[]; //第一组数据
  List<double> datas2 = <double>[]; //第二组数据
  int divIndex = 0; //所在层
  bool isNotEmpty = false;
  bool leftOrRight = true; //依附于左轴或右轴
  double lineWidth = 1; //线的宽度
  String shapeName = ""; //名称
  String shapeType = "line"; //类型
  List<double> showHideDatas = <double>[]; //控制显示隐藏的数据
  String style = ""; //样式
  String text = ""; //显示的文字
  String title = ""; //第一个标题
  String title2 = ""; //第二个标题
  double value = 0; //显示文字的值
  BaseShape() {
    isNotEmpty = true;
  }
}

/*
* 画线工具结构
*/
class FCPlot {
  bool isNotEmpty = false;
  String lineColor = "rgb(0,0,0)"; //线的颜色
  double lineWidth = 1; //线的宽度
  String pointColor = "rgba(0,0,0,120)"; //线的颜色
  String plotType = "Line"; //线的类型
  double key1 = 0; //第一个键
  double key2 = 0; //第二个键
  double key3 = 0; //第三个键
  double startKey1 = 0; //移动前第一个键
  double startValue1 = 0; //移动前第一个值
  double startKey2 = 0; //移动前第二个键
  double startValue2 = 0; //移动前第二个值
  double startKey3 = 0; //移动前第三个键
  double startValue3 = 0; //移动前第三个值
  double value1 = 0; //第一个值
  double value2 = 0; //第二个值
  double value3 = 0; //第三个值
  FCPlot() {
    isNotEmpty = true;
  }
}

/*
* 图表
*/
class FCChart extends FCView {
  bool autoFillHScale = false; //是否填充满X轴
  bool allowDragChartDiv = false; //是否允许拖拽图层
  double candleDistance = 0; //蜡烛线的距离
  double candleMax = 0; //蜡烛线的最大值
  double candleMin = 0; //蜡烛线的最小值
  double candleMaxRight = 0; //蜡烛线的右轴最大值
  double candleMinRight = 0; //蜡烛线的右轴最小值
  String crossTipColor = "rgb(50,50,50)"; //十字线标识的颜色
  String crossLineColor = "rgb(100,100,100)"; //十字线的颜色
  double candlePaddingTop = 30; //图表层的上边距
  double candlePaddingBottom = 30; //图表层的下边距
  int candleDigit = 2; //图表层保留小数的位数
  double candleDivPercent = 0.5; //图表层的占比
  int crossStopIndex = -1; //鼠标停留位置
  String cycle = "second"; //周期
  List<SecurityData> data = <SecurityData>[]; //子节点
  String downColor = "rgb(15,193,118)"; //下跌颜色
  int firstVisibleIndex = -1; //起始可见的索引
  String gridColor = "rgba(255,0,0,120)"; //网格颜色
  int lastVisibleIndex = -1; //结束可见的索引
  double leftVScaleWidth = 80; //左轴宽度
  bool lastRecordIsVisible = true; //最后记录是否可见
  double lastVisibleKey = 0; //最后可见的主键
  double hScalePixel = 11; //蜡烛线的宽度
  double hScaleHeight = 30; //X轴的高度
  double hScaleTextDistance = 10; //X轴的文字间隔
  String hScaleFormat = ""; //X轴的格式化字符，例如YYYY-mm-dd HH:MM:SS
  double indMax = 0; //指标层的最大值
  double indMin = 0; //指标的最小值
  double indMax2 = 0; //指标层2的最大值
  double indMin2 = 0; //指标层2的最小值
  double indMaxRight = 0; //指标层的右轴最大值
  double indMinRight = 0; //指标层的右轴最小值
  double indMax2Right = 0; //指标层2的右轴最大值
  double indMin2Right = 0; //指标层2的右轴最小值
  int indDigit = 2; //指标层保留小数的位数
  int indDigit2 = 2; //指标层2保留小数的位数
  double indDivPercent = 0.3; //指标层的占比
  double indDivPercent2 = 0.0; //指标层2的占比
  double indPaddingTop = 20; //指标层的上边距
  double indPaddingBottom = 20; //指标层的下边距
  double indPaddingTop2 = 20; //指标层2的上边距
  double indPaddingBottom2 = 20; //指标层2的下边距
  List<String> indicatorColors = <String>[]; //扩展图形
  int lastValidIndex = -1; //最后有效数据索引
  double lineWidthChart = 1;
  String mainIndicator = ""; //主图指标
  int magnitude = 1; //成交量的比例
  double offsetX = 0;//横向绘图偏移
  List<FCPlot> plots = <FCPlot>[]; //画线
  double plotPointSizeChart = 20; //画线的选中点大小
  double rightVScaleWidth = 0; //右轴宽度
  double rightSpace = 0; //右侧空间
  String scaleColor = "rgb(255,0,0)"; //刻度的颜色
  String showIndicator = ""; //显示指标
  bool showCrossLine = false; //是否显示十字线
  int selectPlotPoint = -1; //选中画线的点
  bool startMovePlot = false; //选中画线
  String selectShape = ""; //选中的图形
  String selectShapeEx = ""; //选中的图形信息
  List<BaseShape> shapes = <BaseShape>[]; //扩展图形
  List<FCPlot> sPlot = <FCPlot>[]; //选中的线条
  FCPoint touchPosition = FCPoint(0, 0); //鼠标坐标
  double targetOldX = 0; //缩小时旧的位置1
	double targetOldX2 = 0; //放大时旧的位置2
  double volMax = 0; //成交量层的最大值
  double volMin = 0; //成交量层的最小值
  double volMaxRight = 0; //成交量层的右轴最大值
  double volMinRight = 0; //成交量层的右轴最小值
  int volDigit = 0; //成交量层保留小数的位数
  double volDivPercent = 0.2; //成交量层的占比
  double volPaddingTop = 20; //成交量层的上边距
  double volPaddingBottom = 0; //成交量层的下边距
  double vScaleDistance = 35; //纵轴的间隔
  String vScaleType = "standard"; //纵轴的类型 log10代表指数坐标
  String upColor = "rgb(219,68,83)"; //上涨颜色
  double kChart = 0;
  double bChart = 0;
  double oXChart = 0;
  double oYChart = 0;
  double rChart = 0;
  double gridStepChart = 0; //网格计算临时变量
  int gridDigitChart = 0; //网格计算临时变量
  //拖动滚动的缓存
  int firstIndexCacheChart = -1;
  int firstTouchIndexCacheChart = -1;
  FCPoint firstTouchPointCacheChart = FCPoint(0, 0);
  int lastIndexCacheChart = -1;
  int secondTouchIndexCacheChart = -1;
  FCPoint secondTouchPointCacheChart = FCPoint(0, 0);
  double firstPaddingTop = 0;
  double firtstPaddingBottom = 0;
  FCPoint touchDownPointChart = FCPoint(0, 0);
  String addingPlotChart = "";
  double x4Chart = 0;
  double y4Chart = 0;
  double nHighChart = 0;
  double nLowChart = 0;
  double xChart = 0;
  double yChart = 0;
  double wChart = 0;
  double hChart = 0;
  double upSubValue = 0;
  double downSubValue = 0;
  List<double> allema12 = <double>[];
  List<double> allema26 = <double>[];
  List<double> alldifarr = <double>[];
  List<double> alldeaarr = <double>[];
  List<double> allmacdarr = <double>[];
  List<double> bollUp = <double>[];
  List<double> bollDown = <double>[];
  List<double> bollMid = <double>[];
  List<double> bias1 = <double>[];
  List<double> bias2 = <double>[];
  List<double> bias3 = <double>[];
  List<double> kdjK = <double>[];
  List<double> kdjD = <double>[];
  List<double> kdjJ = <double>[];
  List<double> rsi1 = <double>[];
  List<double> rsi2 = <double>[];
  List<double> rsi3 = <double>[];
  List<double> roc = <double>[];
  List<double> rocMa = <double>[];
  List<double> wr1 = <double>[];
  List<double> wr2 = <double>[];
  List<double> cci = <double>[];
  List<double> bbi = <double>[];
  List<double> trix = <double>[];
  List<double> trixMa = <double>[];
  List<double> dma1 = <double>[];
  List<double> dma2 = <double>[];
  List<double> ma5 = <double>[];
  List<double> ma10 = <double>[];
  List<double> ma20 = <double>[];
  List<double> ma30 = <double>[];
  List<double> ma120 = <double>[];
  List<double> ma250 = <double>[];
  List<double> closeArr = <double>[];
  FCPlot getSelectedPlot() {
    if (sPlot.isNotEmpty) {
      return sPlot[0];
    } else {
      FCPlot nonePlot = FCPlot();
      nonePlot.isNotEmpty = false;
      return nonePlot;
    }
  }

  void setSelectedPlot(FCPlot plot) {
    sPlot = <FCPlot>[];
    sPlot.add(plot);
  }

  FCChart() {
    viewType = "chart";
    indicatorColors.add("rgb(100,100,100)");
    indicatorColors.add("rgb(206,147,27)");
    indicatorColors.add("rgb(150,0,150)");
    indicatorColors.add("rgb(255,0,0)");
    indicatorColors.add("rgb(0,150,150)");
    indicatorColors.add("rgb(0,150,0)");
    indicatorColors.add("rgb(59,174,218)");
    indicatorColors.add("rgb(50,50,50)");
    allowDragScroll = true;
  }
}

/*
* 月层
*/
class MonthDiv {
  int aDirection = 0; //动画的方向
  int aTick = 0; //动画当前帧数
  int aTotalTick = 40; //动画总帧数
  bool isNotEmpty = false;
  int month = 0; //月份
  List<MonthButton> monthButtons = <MonthButton>[]; //月的按钮
  List<MonthButton> monthButtonsAm = <MonthButton>[]; //动画日期的集合
  int year = 0; //年份
  MonthDiv() {
    isNotEmpty = true;
  }
}

/*
* 年层
*/
class YearDiv {
  int aDirection = 0; //动画的方向
  int aTick = 0; //动画当前帧数
  int aTotalTick = 40; //动画总帧数
  bool isNotEmpty = false;
  int startYear = 0; //开始年份
  List<YearButton> yearButtons = <YearButton>[]; //年的按钮
  List<YearButton> yearButtonsAm = <YearButton>[]; //动画日期的集合
  YearDiv() {
    isNotEmpty = true;
  }
}

/*
* 头部层
*/
class HeadDiv {
  String arrowColor = "rgb(150,150,150)"; //箭头颜色
  String backColor = "rgb(255,255,255)"; //箭头颜色
  FCRect bounds = FCRect(0, 0, 0, 0); //显示区域
  bool isNotEmpty = false;
  String textColor = "rgb(0,0,0)"; //文字颜色
  String titleFont = "Default,20"; //标题字体
  String weekFont = "Default,14"; //星期字体
  HeadDiv() {
    isNotEmpty = true;
  }
}

/*
* 时间层
*/
class TimeDiv {
  FCRect bounds = FCRect(0, 0, 0, 0); //显示区域
  bool isNotEmpty = false;
  TimeDiv() {
    isNotEmpty = true;
  }
}

/*
* 年的结构
*/
class CYear {
  bool isNotEmpty = false;
  Map<int, CMonth> months = {}; //月的集合
  int year = 0; //年
  CYear() {
    isNotEmpty = true;
  }
}

/*
* 月的结构
*/
class CMonth {
  Map<int, CDay> days = {}; //日的集合
  bool isNotEmpty = false;
  int month = 0; //月
  int year = 0; //年
  CMonth() {
    isNotEmpty = true;
  }
}

/*
* 日的结构
*/
class CDay {
  int day = 0; //日
  bool isNotEmpty = false;
  int month = 0; //月
  int year = 0; //年
  CDay() {
    isNotEmpty = true;
  }
}

/*
* 日历
*/
class FCCalendar extends FCView {
  List<DayDiv> dayDiv = <DayDiv>[]; //日层
  List<HeadDiv> headDiv = <HeadDiv>[]; //头部层
  List<MonthDiv> monthDiv = <MonthDiv>[]; //月层
  String mode = "day"; //模式
  List<CDay> selectedDay = <CDay>[]; //选中日
  List<TimeDiv> timeDiv = <TimeDiv>[]; //时间层
  bool useAnimation = false; //是否使用动画
  List<YearDiv> yearDiv = <YearDiv>[]; //年层
  Map<int, CYear> years = {}; //日历
  CDay getSelectedDay() {
    if (selectedDay.isNotEmpty) {
      return selectedDay[0];
    } else {
      CDay noneDay = CDay();
      noneDay.isNotEmpty = false;
      return noneDay;
    }
  }

  void setSelectedDay(CDay d) {
    selectedDay = <CDay>[];
    selectedDay.add(d);
  }

  DayDiv getDayDiv() {
    if (dayDiv.isNotEmpty) {
      return dayDiv[0];
    } else {
      DayDiv noneDayDiv = DayDiv();
      noneDayDiv.isNotEmpty = false;
      return noneDayDiv;
    }
  }

  void setDayDiv(DayDiv d) {
    dayDiv = <DayDiv>[];
    dayDiv.add(d);
  }

  HeadDiv getHeadDiv() {
    if (headDiv.isNotEmpty) {
      return headDiv[0];
    } else {
      HeadDiv noneHeadDiv = HeadDiv();
      noneHeadDiv.isNotEmpty = false;
      return noneHeadDiv;
    }
  }

  void setHeadDiv(HeadDiv h) {
    headDiv = <HeadDiv>[];
    headDiv.add(h);
  }

  MonthDiv getMonthDiv() {
    if (monthDiv.isNotEmpty) {
      return monthDiv[0];
    } else {
      MonthDiv noneMonthDiv = MonthDiv();
      noneMonthDiv.isNotEmpty = false;
      return noneMonthDiv;
    }
  }

  void setMonthDiv(MonthDiv m) {
    monthDiv = <MonthDiv>[];
    monthDiv.add(m);
  }

  YearDiv getYearDiv() {
    if (yearDiv.isNotEmpty) {
      return yearDiv[0];
    } else {
      YearDiv noneYearDiv = YearDiv();
      noneYearDiv.isNotEmpty = false;
      return noneYearDiv;
    }
  }

  void setYearDiv(YearDiv y) {
    yearDiv = <YearDiv>[];
    yearDiv.add(y);
  }

  TimeDiv getTimeDiv() {
    if (timeDiv.isNotEmpty) {
      return timeDiv[0];
    } else {
      TimeDiv noneTimeDiv = TimeDiv();
      noneTimeDiv.isNotEmpty = false;
      return noneTimeDiv;
    }
  }

  void setTimeDiv(TimeDiv td) {
    timeDiv = <TimeDiv>[];
    timeDiv.add(td);
  }

  FCCalendar() {
    viewType = "calendar";
  }
}

/*
* 日的按钮
*/
class DayButton {
  String backColor = "none"; //背景颜色
  String borderColor = "rgb(150,150,150)"; //文字颜色
  FCRect bounds = FCRect(0, 0, 0, 0); //显示区域
  List<CDay> day = <CDay>[]; //日期
  String font = "Default,16"; //字体
  bool inThisMonth = false; //是否在本月
  bool isNotEmpty = false;
  bool selected = false; //是否被选中
  String textColor = "rgb(0,0,0)"; //文字颜色
  String textColor2 = "rgb(50,50,50)"; //第二个文字颜色
  bool visible = true; //是否可见
  DayButton() {
    isNotEmpty = true;
  }
  CDay getDay() {
    if (day.isNotEmpty) {
      return day[0];
    } else {
      CDay noneDay = CDay();
      noneDay.isNotEmpty = false;
      return noneDay;
    }
  }

  void setDay(CDay d) {
    day = <CDay>[];
    day.add(d);
  }
}

/*
* 月的按钮
*/
class MonthButton {
  String backColor = "none"; //背景颜色
  String borderColor = "rgb(150,150,150)"; //文字颜色
  FCRect bounds = FCRect(0, 0, 0, 0); //显示区域
  String font = "Default,16"; //字体
  bool isNotEmpty = false;
  int month = 0; //月
  String textColor = "rgb(0,0,0)"; //文字颜色
  bool visible = true; //是否可见
  int year = 0; //年
  MonthButton() {
    isNotEmpty = true;
  }
}

/*
* 年的按钮
*/
class YearButton {
  String backColor = "none"; //背景颜色
  String borderColor = "rgb(150,150,150)"; //文字颜色
  FCRect bounds = FCRect(0, 0, 0, 0); //显示区域
  String font = "Default,16"; //字体
  bool isNotEmpty = false;
  String textColor = "rgb(0,0,0)"; //文字颜色
  bool visible = true; //是否可见
  int year = 0; //年
  YearButton() {
    isNotEmpty = true;
  }
}

/*
* 日期层
*/
class DayDiv {
  int aClickRowFrom = 0; //点击时的上月的行
  int aClickRowTo = 0; //点击时的当月的行
  int aDirection = 0; //动画的方向
  int aTick = 0; //动画当前帧数
  int aTotalTick = 40; //动画总帧数
  List<DayButton> dayButtons = <DayButton>[]; //日期的集合
  List<DayButton> dayButtonsAm = <DayButton>[]; //动画日期的集合
  bool isNotEmpty = false;
  DayDiv() {
    isNotEmpty = true;
  }
}

/*
* 菜单
*/
class FCMenu extends FCLayoutDiv {
  bool autoSize = true; //是否自动适应尺寸
  List<FCComboBox> comboBox = <FCComboBox>[]; //所在的下拉菜单
  List<FCMenuItem> items = <FCMenuItem>[]; //菜单项
  bool popup = true; //是否弹出
  FCMenu() {
    viewType = "menu";
    size = FCSize(100, 100);
    super.showHScrollBar = true;
    super.showVScrollBar = true;
    super.visible = false;
    super.layoutStyle = "toptobottom";
    super.autoWrap = false;
    super.maximumSize = FCSize(100, 300);
    super.allowPreviewsEvent = true;
  }
  FCComboBox getComboBox() {
    if (comboBox.isNotEmpty) {
      return comboBox[0];
    } else {
      FCComboBox noneComboBox = FCComboBox();
      noneComboBox.isNotEmpty = false;
      return noneComboBox;
    }
  }

  void setComboBox(FCComboBox c) {
    comboBox = <FCComboBox>[];
    comboBox.add(c);
  }
}

/*
* 菜单项
*/
class FCMenuItem extends FCView {
  bool checked = false; //是否选中
  List<FCMenu> dropDownMenu = <FCMenu>[]; //下拉菜单
  List<FCMenuItem> items = <FCMenuItem>[]; //菜单项
  List<FCMenu> parentMenu = <FCMenu>[]; //所在菜单
  List<FCMenuItem> parentItem = <FCMenuItem>[]; //父菜单项
  String value = ""; //值
  FCMenuItem() {
    viewType = "menuitem";
    hoveredColor = "rgb(150,150,150)";
    pushedColor = "rgb(200,200,200)";
    size = FCSize(100, 25);
  }
  FCMenu getDropDownMenu() {
    if (dropDownMenu.isNotEmpty) {
      return dropDownMenu[0];
    } else {
      FCMenu noneMenu = FCMenu();
      noneMenu.isNotEmpty = false;
      return noneMenu;
    }
  }

  void setDropDownMenu(FCMenu m) {
    dropDownMenu = <FCMenu>[];
    dropDownMenu.add(m);
  }

  FCMenu getParentMenu() {
    if (parentMenu.isNotEmpty) {
      return parentMenu[0];
    } else {
      FCMenu noneMenu = FCMenu();
      noneMenu.isNotEmpty = false;
      return noneMenu;
    }
  }

  void setParentMenu(FCMenu m) {
    parentMenu = <FCMenu>[];
    parentMenu.add(m);
  }

  FCMenuItem getParentItem() {
    if (parentItem.isNotEmpty) {
      return parentItem[0];
    } else {
      FCMenuItem noneItem = FCMenuItem();
      noneItem.isNotEmpty = false;
      return noneItem;
    }
  }

  void setParentItem(FCMenuItem m) {
    parentItem = <FCMenuItem>[];
    parentItem.add(m);
  }
}

/*
* 下拉选择
*/
class FCComboBox extends FCView {
  int selectedIndex = -1; //选中索引
  List<FCMenu> dropDownMenu = <FCMenu>[]; //下拉菜单
  FCComboBox() {
    viewType = "combobox";
  }
  FCMenu getDropDownMenu() {
    if (dropDownMenu.isNotEmpty) {
      return dropDownMenu[0];
    } else {
      FCMenu noneMenu = FCMenu();
      noneMenu.isNotEmpty = false;
      return noneMenu;
    }
  }

  void setDropDownMenu(FCMenu m) {
    dropDownMenu = <FCMenu>[];
    dropDownMenu.add(m);
  }
}

/*
* 绘图结构
*/
class FCPaint {
  bool cancelClick = false; //是否退出点击
  late Canvas canvas; //视图
  String defaultUIStyle = "light"; //默认样式
  FCPoint dragBeginPoint = FCPoint(0, 0); //拖动开始时的触摸位置
  FCRect dragBeginRect = FCRect(0, 0, 0, 0); //拖动开始时的区域
  List<FCView> draggingView = <FCView>[]; //正被拖动的控件
  List<FCView> focusedView = <FCView>[]; //焦点视图
  bool isDoubleClick = false; //是否双击
  bool isPath = false; //是否路径
  int isPainting = 0; //是否正在绘图
  bool isNotEmpty = false;
  int lastClickTime = 0; //上次点击时间
  bool moveTo = false;
  double offsetX = 0; //横向偏移
  double offsetY = 0; //纵向偏移
  late Paint paint; //绘图对象
  FCRect paintRect = FCRect(0, 0, 0, 0); //绘图区域
  late Path path; //路径
  int resizeColumnState = 0; //改变列宽度的状态
  double resizeColumnBeginWidth = 0; //改变列宽度的起始值
  int resizeColumnIndex = -1; //改变列宽度的索引
  double scaleFactorX = 1; //横向缩放比例
  double scaleFactorY = 1; //纵向缩放比例
  FCSize size = FCSize(0, 0); //大小
  String systemFont = "Arial"; //系统字体
  List<FCView> touchDownView = <FCView>[]; //鼠标按下的视图
  List<FCView> touchMoveView = <FCView>[]; //鼠标移动的视图
  FCPoint touchDownPoint = FCPoint(0, 0);
  List<FCView> views = <FCView>[]; //视图集合
  Map<String, FCSize> textSizeCache = {}; 
  late void Function(FCView view, FCPaint paint, FCRect clipRect) onPaint;
  late void Function(FCView view, FCPaint paint, FCRect clipRect) onPaintBorder;
  late void Function(FCView view, bool firstTouch, FCPoint firstPoint,
      bool secondTouch, FCPoint secondPoint) onTouchBegin;
  late void Function(FCView view, bool firstTouch, FCPoint firstPoint,
      bool secondTouch, FCPoint secondPoint) onTouchMove;
  late void Function(FCView view, bool firstTouch, FCPoint firstPoint,
      bool secondTouch, FCPoint secondPoint) onTouchEnd;
  late void Function(
      FCView view, bool firstTouch, FCPoint firstPoint,
      bool secondTouch, FCPoint secondPoint, int clicks) onClick;
  late void Function(List<FCView> views) onUpdateView;
  late void Function(
      FCGrid grid,
      FCGridRow row,
      FCGridColumn column,
      FCGridCell cell,
      FCPaint paint,
      double left,
      double top,
      double right,
      double bottom) onPaintGridCell;
  late void Function(FCGrid grid, FCGridColumn column, FCPaint paint,
      double left, double top, double right, double bottom) onPaintGridColumn;
  late void Function(
      FCGrid grid,
      FCGridRow row,
      FCGridColumn gridColumn,
      FCGridCell cell,
      bool firstTouch,
      FCPoint firstPoint,
      bool secondTouch,
      FCPoint secondPoint,
      int clicks) onClickGridCell;
  late void Function(
      FCGrid grid,
      FCGridColumn gridColumn,
      bool firstTouch,
      FCPoint firstPoint,
      bool secondTouch,
      FCPoint secondPoint,
      int clicks) onClickGridColumn;
  late void Function(
      FCTree tree,
      FCTreeRow row,
      FCTreeColumn column,
      FCTreeNode node,
      FCPaint paint,
      double left,
      double top,
      double right,
      double bottom) onPaintTreeNode;
  late void Function(
      FCTree tree,
      FCTreeNode node,
      bool firstTouch,
      FCPoint firstPoint,
      bool secondTouch,
      FCPoint secondPoint, 
      int clicks) onClickTreeNode;
  late void Function(FCChart chart, FCPaint paint, FCRect clipRect)
      onPaintChartScale;
  late void Function(FCChart chart, FCPaint paint, FCRect clipRect)
      onPaintChartHScale;
  late void Function(FCChart chart, FCPaint paint, FCRect clipRect)
      onPaintChartStock;
  late void Function(FCChart chart, FCPaint paint, FCRect clipRect)
      onPaintChartPlot;
  late void Function(FCChart chart, FCPaint paint, FCRect clipRect)
      onPaintChartCrossLine;
  late void Function(FCChart chart) onCalculateChartMaxMin;
  late void Function(FCCalendar calendar, DayButton dayButton, FCPaint paint)
      onPaintCalendarDayButton;
  late void Function(
          FCCalendar calendar, MonthButton monthButton, FCPaint paint)
      onPaintCalendarMonthButton;
  late void Function(FCCalendar calendar, YearButton yearButton, FCPaint paint)
      onPaintCalendarYearButton;
  late void Function(FCCalendar calendar, HeadDiv headDiv, FCPaint paint)
      onPaintHeadDiv;
  late bool Function(FCView view, FCPoint mp) onContainsPoint;
  bool hasPaintEvent = false;
  bool hasPaintBorderEvent = false;
  bool hasTouchBeginEvent = false;
  bool hasTouchMoveEvent = false;
  bool hasTouchEndEvent = false;
  bool hasClickEvent = false;
  bool hasUpdateViewEvent = false;
  bool hasPaintGridCellEvent = false;
  bool hasPaintGridColumnEvent = false;
  bool hasClickGridCellEvent = false;
  bool hasClickGridColumnEvent = false;
  bool hasPaintTreeNodeEvent = false;
  bool hasClickTreeNodeEvent = false;
  bool hasPaintChartScaleEvent = false;
  bool hasPaintChartHScaleEvent = false;
  bool hasPaintChartStockEvent = false;
  bool hasPaintChartPlotEvent = false;
  bool hasPaintChartCrossLineEvent = false;
  bool hasCalculateChartMaxMinEvent = false;
  bool hasPaintCalendarDayButtonEvent = false;
  bool hasPaintCalendarMonthButtonEvent = false;
  bool hasPaintCalendarYearButtonEvent = false;
  bool hasPaintHeadDivEvent = false;
  bool hasContainsPointEvent = false;
  void addPaintEvent(
      void Function(FCView view, FCPaint paint, FCRect clipRect) callBack) {
    hasPaintEvent = true;
    onPaint = callBack;
  }

  void addPaintBorderEvent(
      void Function(FCView view, FCPaint paint, FCRect clipRect) callBack) {
    hasPaintBorderEvent = true;
    onPaintBorder = callBack;
  }

  void addTouchBeginEvent(
      void Function(FCView view, bool firstTouch, FCPoint firstPoint,
              bool secondTouch, FCPoint secondPoint)
          callBack) {
    hasTouchBeginEvent = true;
    onTouchBegin = callBack;
  }

  void addTouchMoveEvent(
      void Function(FCView view, bool firstTouch, FCPoint firstPoint,
              bool secondTouch, FCPoint secondPoint)
          callBack) {
    hasTouchMoveEvent = true;
    onTouchMove = callBack;
  }

  void addTouchEndEvent(
      void Function(FCView view, bool firstTouch, FCPoint firstPoint,
              bool secondTouch, FCPoint secondPoint)
          callBack) {
    hasTouchEndEvent = true;
    onTouchEnd = callBack;
  }

  void addClickEvent(
      void Function(FCView view, bool firstTouch, FCPoint firstPoint,
              bool secondTouch, FCPoint secondPoint, int clicks) callBack) {
    hasClickEvent = true;
    onClick = callBack;
  }

  void addUpdateViewEvent(void Function(List<FCView> views) callBack) {
    hasUpdateViewEvent = true;
    onUpdateView = callBack;
  }

  void addPaintGridCellEvent(
      void Function(
              FCGrid grid,
              FCGridRow row,
              FCGridColumn column,
              FCGridCell cell,
              FCPaint paint,
              double left,
              double top,
              double right,
              double bottom)
          callBack) {
    hasPaintGridCellEvent = true;
    onPaintGridCell = callBack;
  }

  void addPaintGridColumn(
      void Function(FCGrid grid, FCGridColumn column, FCPaint paint,
              double left, double top, double right, double bottom)
          callBack) {
    hasPaintGridColumnEvent = true;
    onPaintGridColumn = callBack;
  }

  void addClickGridCell(
      void Function(
              FCGrid grid,
              FCGridRow row,
              FCGridColumn gridColumn,
              FCGridCell cell,
              bool firstTouch,
              FCPoint firstPoint,
              bool secondTouch,
              FCPoint secondPoint,
              int clicks)
          callBack) {
    hasClickGridCellEvent = true;
    onClickGridCell = callBack;
  }

  void addClickGridColumn(
      void Function(FCGrid grid, FCGridColumn gridColumn, bool firstTouch,
              FCPoint firstPoint, bool secondTouch, FCPoint secondPoint, int clicks)
          callBack) {
    hasClickGridColumnEvent = true;
    onClickGridColumn = callBack;
  }

  void addPaintTreeNodeCallBack(
      void Function(
              FCTree tree,
              FCTreeRow row,
              FCTreeColumn column,
              FCTreeNode node,
              FCPaint paint,
              double left,
              double top,
              double right,
              double bottom)
          callBack) {
    hasPaintTreeNodeEvent = true;
    onPaintTreeNode = callBack;
  }

  void addClickTreeNode(
      void Function(FCTree tree, FCTreeNode node, bool firstTouch,
              FCPoint firstPoint, bool secondTouch, FCPoint secondPoint, int clicks)
          callBack) {
    hasClickTreeNodeEvent = true;
    onClickTreeNode = callBack;
  }

  void addPaintChartScaleEvent(
      void Function(FCChart chart, FCPaint paint, FCRect clipRect) callBack) {
    hasPaintChartScaleEvent = true;
    onPaintChartScale = callBack;
  }

  void addPaintChartHScaleEvent(
      void Function(FCChart chart, FCPaint paint, FCRect clipRect) callBack) {
    hasPaintChartHScaleEvent = true;
    onPaintChartHScale = callBack;
  }

  void addPaintChartStockEvent(
      void Function(FCChart chart, FCPaint paint, FCRect clipRect) callBack) {
    hasPaintChartStockEvent = true;
    onPaintChartStock = callBack;
  }

  void addPaintChartPlotEvent(
      void Function(FCChart chart, FCPaint paint, FCRect clipRect) callBack) {
    hasPaintChartPlotEvent = true;
    onPaintChartPlot = callBack;
  }

  void addPaintChartCrossLineEvent(
      void Function(FCChart chart, FCPaint paint, FCRect clipRect) callBack) {
    hasPaintChartCrossLineEvent = true;
    onPaintChartCrossLine = callBack;
  }

  void addCalculateChartMaxMinEvent(void Function(FCChart chart) callBack) {
    hasCalculateChartMaxMinEvent = true;
    onCalculateChartMaxMin = callBack;
  }

  void addPaintCalendarDayButtonEvent(
      void Function(FCCalendar calendar, DayButton dayButton, FCPaint paint)
          callBack) {
    hasPaintCalendarDayButtonEvent = true;
    onPaintCalendarDayButton = callBack;
  }

  void addPaintCalendarMonthButtonEvent(
      void Function(FCCalendar calendar, MonthButton monthButton, FCPaint paint)
          callBack) {
    hasPaintCalendarMonthButtonEvent = true;
    onPaintCalendarMonthButton = callBack;
  }

  void addPaintCalendarYearButtonEvent(
      void Function(FCCalendar calendar, YearButton yearButton, FCPaint paint)
          callBack) {
    hasPaintCalendarYearButtonEvent = true;
    onPaintCalendarYearButton = callBack;
  }

  void addPaintHeadDivEvent(
      void Function(FCCalendar calendar, HeadDiv headDiv, FCPaint paint)
          callBack) {
    hasPaintHeadDivEvent = true;
    onPaintHeadDiv = callBack;
  }

  void addContainsPointEvent(
      bool Function(FCView view, FCPoint mp)
          callBack) {
    hasContainsPointEvent = true;
    onContainsPoint = callBack;
  }

  FCPaint() {
    isNotEmpty = true;
  }

  FCView getTouchDownView() {
    if (touchDownView.isNotEmpty) {
      return touchDownView[0];
    } else {
      FCView noneView = FCView();
      noneView.isNotEmpty = false;
      return noneView;
    }
  }

  void setTouchDownView(FCView view) {
    touchDownView = <FCView>[];
    touchDownView.add(view);
  }

  FCView getTouchMoveView() {
    if (touchMoveView.isNotEmpty) {
      return touchMoveView[0];
    } else {
      FCView noneView = FCView();
      noneView.isNotEmpty = false;
      return noneView;
    }
  }

  void setTouchMoveView(FCView view) {
    touchMoveView = <FCView>[];
    touchMoveView.add(view);
  }

  FCView getFocusedView() {
    if (focusedView.isNotEmpty) {
      return focusedView[0];
    } else {
      FCView noneView = FCView();
      noneView.isNotEmpty = false;
      return noneView;
    }
  }

  void setFocusedView(FCView view) {
    focusedView = <FCView>[];
    focusedView.add(view);
  }

  FCView getDraggingView() {
    if (draggingView.isNotEmpty) {
      return draggingView[0];
    } else {
      FCView noneView = FCView();
      noneView.isNotEmpty = false;
      return noneView;
    }
  }

  void setDraggingView(FCView view) {
    draggingView = <FCView>[];
    draggingView.add(view);
  }

  /*
  * 添加线
  * x1:横坐标1
  * y1:纵坐标1
  * x2:横坐标2
  * y2:纵坐标2
  */
  void addLine(double x1, double y1, double x2, double y2) {
    if (!moveTo) {
      moveTo = true;
      path.moveTo((x1 + offsetX) * scaleFactorX, (y1 + offsetY) * scaleFactorY);
    }
    path.lineTo((x2 + offsetX) * scaleFactorX, (y2 + offsetY) * scaleFactorY);
  }

  /*
  * 开始路径
  */
  void beginPath() {
    path = Path();
    isPath = true;
  }

  /*
  * 开始绘图
  * rect:区域
  */
  void beginPaint() {
    paint = Paint();
  }

  /*
  * 闭合路径
  */
  void closeFigure() {
    path.close();
  }

  /*
  * 关闭路径
  */
  void closePath() {
    moveTo = false;
  }

  /*
  * 绘制线
  * color:颜色
  * width:宽度
  * style:样式
  * x1:横坐标1
  * y1:纵坐标1
  * x2:横坐标2
  * y2:纵坐标2
  */
  void drawLine(String color, double width, double style, double x1, double y1,
      double x2, double y2) {
    paint.strokeWidth = min(scaleFactorX, scaleFactorY) * width;
    paint.color = stringToColor(color);
    paint.style = PaintingStyle.stroke;
    canvas.drawLine(
        Offset((x1 + offsetX) * scaleFactorX, (y1 + offsetY) * scaleFactorY),
        Offset((x2 + offsetX) * scaleFactorX, (y2 + offsetY) * scaleFactorY),
        paint);
  }

  /*
  * 绘制路径
  * color:颜色
  * width:宽度
  * style:样式
  */
  void drawPath(String color, double width, double style) {
    paint.strokeWidth = min(scaleFactorX, scaleFactorY) * width;
    paint.color = stringToColor(color);
    paint.style = PaintingStyle.stroke;
    canvas.drawPath(path, paint);
  }

  /*
  * 绘制连续线条
  * color:颜色
  * width:宽度
  * style:样式
  */
  void drawPolyline(String color, double width, double style, List<FCPoint> apt) {
    if(apt.length > 1){
      paint.strokeWidth = min(scaleFactorX, scaleFactorY) * width;
      paint.color = stringToColor(color);
      paint.style = PaintingStyle.stroke;
      path = Path();
      for(int i = 0; i < apt.length; i++){
        double x = apt[i].x;
        double y = apt[i].y;
        x = x + offsetX;
        y = y + offsetY;
        if (scaleFactorX != 1 || scaleFactorY != 1){
            x = scaleFactorX * x;
            y = scaleFactorY * y;
        }
        if (i == 0){
            path.moveTo(x, y);
        }else{
            path.lineTo(x, y);
        }
      }
      canvas.drawPath(path, paint);
    }
  }

  /*
  * 绘制多边形
  * color:颜色
  * width:宽度
  * style:样式
  */
  void drawPolygon(String color, double width, double style, List<FCPoint> apt) {
    if(apt.length > 1){
      paint.strokeWidth = min(scaleFactorX, scaleFactorY) * width;
      paint.color = stringToColor(color);
      paint.style = PaintingStyle.stroke;
      path = Path();
      for(int i = 0; i < apt.length; i++){
        double x = apt[i].x;
        double y = apt[i].y;
        x = x + offsetX;
        y = y + offsetY;
        if (scaleFactorX != 1 || scaleFactorY != 1){
            x = scaleFactorX * x;
            y = scaleFactorY * y;
        }
        if (i == 0){
            path.moveTo(x, y);
        }else{
            path.lineTo(x, y);
        }
      }
      path.close();
      canvas.drawPath(path, paint);
    }
  }

  /*
  * 绘制矩形
  * image:图片
  * left:左侧坐标
  * top:上方坐标
  * right:右侧坐标
  * bottom:下方坐标
  */
  void drawImage(ui.Image image, double left,
      double top, double right, double bottom) {
    double w = right - left;
    double h = bottom - top;
    Rect src = Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble());
    Rect dest = Rect.fromLTWH((left + offsetX) * scaleFactorX,
            (top + offsetY) * scaleFactorY, w * scaleFactorX, h * scaleFactorY);
    canvas.drawImageRect(image, src, dest, paint);
  }

  /*
  * 绘制矩形
  * color:颜色
  * width:宽度
  * style:样式
  * left:左侧坐标
  * top:上方坐标
  * right:右侧坐标
  * bottom:下方坐标
  */
  void drawRect(String color, double width, double style, double left,
      double top, double right, double bottom) {
    paint.strokeWidth = min(scaleFactorX, scaleFactorY) * width;
    paint.color = stringToColor(color);
    paint.style = PaintingStyle.stroke;
    double w = right - left;
    double h = bottom - top;
    canvas.drawRect(
        Rect.fromLTWH((left + offsetX) * scaleFactorX,
            (top + offsetY) * scaleFactorY, w * scaleFactorX, h * scaleFactorY),
        paint);
  }

  /*
  * 绘制圆角矩形
  * color:颜色
  * width:宽度
  * style:样式
  * left:左侧坐标
  * top:上方坐标
  * right:右侧坐标
  * bottom:下方坐标
  * cornerRadius:圆角
  */
  void drawRoundRect(String color, double width, double style, double left,
      double top, double right, double bottom, double cornerRadius) {
    paint.strokeWidth = min(scaleFactorX, scaleFactorY) * width;
    paint.color = stringToColor(color);
    paint.style = PaintingStyle.stroke;
    double w = right - left;
    double h = bottom - top;
    if(cornerRadius > 0){
        canvas.drawRRect(
          RRect.fromRectAndRadius(Rect.fromLTWH((left + offsetX) * scaleFactorX,
              (top + offsetY) * scaleFactorY, w * scaleFactorX, h * scaleFactorY),
              Radius.circular(cornerRadius * min(scaleFactorX, scaleFactorY))),
          paint);
    }else{
      canvas.drawRect(
          Rect.fromLTWH((left + offsetX) * scaleFactorX,
              (top + offsetY) * scaleFactorY, w * scaleFactorX, h * scaleFactorY),
          paint);
    }
  }

  /*
  * 绘制椭圆
  * color:颜色
  * width:宽度
  * style:样式
  * left:左侧坐标
  * top:上方坐标
  * right:右侧坐标
  * bottom:下方坐标
  */
  void drawEllipse(String color, double width, double style, double left,
      double top, double right, double bottom) {
    paint.strokeWidth = min(scaleFactorX, scaleFactorY) * width;
    paint.color = stringToColor(color);
    paint.style = PaintingStyle.stroke;
    double w = right - left;
    double h = bottom - top;
    canvas.drawOval(
        Rect.fromLTWH((left + offsetX) * scaleFactorX,
            (top + offsetY) * scaleFactorY, w * scaleFactorX, h * scaleFactorY),
        paint);
  }

  /*
  * 绘制文字
  * text:文字
  * color:颜色
  * font:字体
  * x:横坐标
  * y:纵坐标
  */
  void drawText(String text, String color, String font, double x, double y) {
    List<String> strs = font.split(",");
    String fontFamily = strs[0];
    if(fontFamily == "Default"){
      fontFamily = systemFont;
    }
    double fontSize = (scaleFactorX + scaleFactorY) / 2 * double.parse(strs[1]);
    final textStyle =
        ui.TextStyle(fontFamily:fontFamily, color: stringToColor(color), fontSize: fontSize);
    final paragraphStyle = ui.ParagraphStyle(textDirection: TextDirection.ltr);
    final paragraphBuilder = ui.ParagraphBuilder(paragraphStyle);
    paragraphBuilder.pushStyle(textStyle);
    paragraphBuilder.addText(text);
    const constraints = ui.ParagraphConstraints(width: 300);
    final paragraph = paragraphBuilder.build();
    paragraph.layout(constraints);
    canvas.drawParagraph(paragraph,
        Offset((x + offsetX) * scaleFactorX, (y + offsetY) * scaleFactorY));
  }

  /*
  * 结束绘图
  */
  void endPaint() {}
  /*
  * 填充路径
  * color:颜色
  */
  void fillPath(String color) {
    paint.color = stringToColor(color);
    paint.style = PaintingStyle.fill;
    path = Path();
    path.close();
    canvas.drawPath(path, paint);
  }

  /*
  * 填充矩形
  * color:颜色
  * left:左侧坐标
  * top:上方坐标
  * right:右侧坐标
  * bottom:下方坐标
  */
  void fillRect(
      String color, double left, double top, double right, double bottom) {
    paint.color = stringToColor(color);
    paint.style = PaintingStyle.fill;
    double w = right - left;
    double h = bottom - top;
    canvas.drawRect(
        Rect.fromLTWH((left + offsetX) * scaleFactorX,
            (top + offsetY) * scaleFactorY, w * scaleFactorX, h * scaleFactorY),
        paint);
  }

  /*
  * 填充多边形
  * color:颜色
  * width:宽度
  * style:样式
  */
  void fillPolygon(String color, List<FCPoint> apt) {
    if(apt.length > 1){
      paint.color = stringToColor(color);
      paint.style = PaintingStyle.fill;
      path = Path();
      for(int i = 0; i < apt.length; i++){
        double x = apt[i].x;
        double y = apt[i].y;
        x = x + offsetX;
        y = y + offsetY;
        if (scaleFactorX != 1 || scaleFactorY != 1){
            x = scaleFactorX * x;
            y = scaleFactorY * y;
        }
        if (i == 0){
            path.moveTo(x, y);
        }else{
            path.lineTo(x, y);
        }
      }
      path.close();
      canvas.drawPath(path, paint);
    }
  }

  /*
  * 填充矩形
  * color:颜色
  * left:左侧坐标
  * top:上方坐标
  * right:右侧坐标
  * bottom:下方坐标
  * cornerRadius:圆角
  */
  void fillRoundRect(
      String color, double left, double top, double right, double bottom, double cornerRadius) {
    paint.color = stringToColor(color);
    paint.style = PaintingStyle.fill;
    double w = right - left;
    double h = bottom - top;
    if(cornerRadius > 0){
        canvas.drawRRect(
          RRect.fromRectAndRadius(Rect.fromLTWH((left + offsetX) * scaleFactorX,
              (top + offsetY) * scaleFactorY, w * scaleFactorX, h * scaleFactorY),
              Radius.circular(cornerRadius * min(scaleFactorX, scaleFactorY))),
          paint);
    }else{
      canvas.drawRect(
          Rect.fromLTWH((left + offsetX) * scaleFactorX,
              (top + offsetY) * scaleFactorY, w * scaleFactorX, h * scaleFactorY),
          paint);
    }
  }

  /*
  * 填充椭圆
  * color:颜色
  * left:左侧坐标
  * top:上方坐标
  * right:右侧坐标
  * bottom:下方坐标
  */
  void fillEllipse(
      String color, double left, double top, double right, double bottom) {
    paint.color = stringToColor(color);
    paint.style = PaintingStyle.fill;
    double w = right - left;
    double h = bottom - top;
    canvas.drawOval(
        Rect.fromLTWH((left + offsetX) * scaleFactorX,
            (top + offsetY) * scaleFactorY, w * scaleFactorX, h * scaleFactorY),
        paint);
  }

  /*
  * 裁剪
  * left:左侧坐标
  * top:上方坐标
  * right:右侧坐标
  * bottom:下方坐标
  */
  void setClip(double left, double top, double right, double bottom) {
    double w = right - left;
    double h = bottom - top;
    Rect rect = Rect.fromLTWH((left + offsetX) * scaleFactorX,
        (top + offsetY) * scaleFactorY, w * scaleFactorX, h * scaleFactorY);
    Path cPath = Path();
    cPath.addRect(rect);
    canvas.clipPath(cPath);
  }

  /*
  * 设置偏移量
  * offsetX:横向偏移
  * offsetY:纵向偏移
  */
  void setOffset(double x, double y) {
    offsetX = x;
    offsetY = y;
  }

  /*
  * 获取字体大小
  * text:文字
  * font:字体
  */
  FCSize textSize(String text, String font) {
    String key = text + font;
    if (textSizeCache.containsKey(key)) {
      return textSizeCache[key] as FCSize;
    }else{
      List<String> strs = font.split(",");
      String fontFamily = strs[0];
      if(fontFamily == "Default"){
        fontFamily = systemFont;
      }
      double fontSize = double.parse(strs[1]);
      double rate = (scaleFactorX + scaleFactorY) / 2;
      fontSize = fontSize * rate;
      final TextPainter textPainter = TextPainter(
          text: TextSpan(
              text: text,
              style: TextStyle(fontFamily:fontFamily, color: Colors.white, fontSize: fontSize)),
          maxLines: 1,
          textDirection: TextDirection.ltr)
        ..layout(minWidth: 0, maxWidth: double.infinity);
      FCSize tSize = FCSize(textPainter.size.width / rate, textPainter.size.height / rate);
      textSizeCache[key] = tSize;
      return tSize;
    }
  }

  /*
  * 绘制文字
  * text:文字
  * color:颜色
  * font:字体
  * left:左侧坐标
  * top:上方坐标
  * right:右侧坐标
   bottom:下方坐标
  */
  void drawTextAutoEllipsis(String text, String color, String font, double left,
      double top, double right, double bottom) {
    if (text.isNotEmpty) {
      FCSize tSize = textSize(text, font);
      if (tSize.cx < right - left) {
        drawText(text, color, font, left, top);
      } else {
        if (tSize.cx > 0) {
          int subLen = 3;
          while (true) {
            int newLen = text.length - subLen;
            if (newLen > 0) {
              String newText = text.substring(0, newLen) + "...".toString();
              tSize = textSize(newText, font);
              if (tSize.cx < right - left) {
                drawText(newText, color, font, left, top);
                break;
              } else {
                subLen += 3;
              }
            } else {
              break;
            }
          }
        }
      }
    }
  }

  /*
  * 保存状态
  */
  void save() {
    canvas.save();
  }

  /*
  * 恢复状态
  */
  void restore() {
    canvas.restore();
  }
}

/*
* 添加顶层视图
* view 视图
* paint 绘图对象
*/
void addView(FCView view, FCPaint paint) {
  view.setPaint(paint);
  paint.views.add(view);
}

/*
* 添加到父视图
* view 视图
* parent 父视图
*/
void addViewToParent(FCView view, FCView parent) {
  view.setPaint(parent.getPaint());
  parent.views.add(view);
  view.setParent(parent);
}

/*
* 插入到顶层视图
* view 视图
* paint 绘图对象
* index 索引
*/
void insertView(FCView view, FCPaint paint, int index) {
  view.setPaint(paint);
  paint.views.insert(index, view);
}

/*
* 插入到到父视图
* view 视图
* parent 父视图
* index 索引
*/
void insertViewToParent(FCView view, FCView parent, int index) {
  view.setPaint(parent.getPaint());
  parent.views.insert(index, view);
  view.setParent(parent);
}

/*
* 清除视图
* paint 绘图对象
*/
void clearViews(FCPaint paint){
  paint.views = <FCView>[];
}

/*
* 移除顶层视图
* view 视图
* paint 绘图对象
*/
void removeView(FCView view, FCPaint paint) {
  if (paint.views.isNotEmpty) {
    for (int i = 0; i < paint.views.length; i++) {
      if (paint.views[i] == view) {
        paint.views.removeAt(i);
        break;
      }
    }
  }
}

/*
* 从父视图中移除
* view 视图
* parent 父视图
*/
void removeViewFromParent(FCView view, FCView parent) {
  if (parent.views.isNotEmpty) {
    for (int i = 0; i < parent.views.length; i++) {
      if (parent.views[i] == view) {
        parent.views.removeAt(i);
        break;
      }
    }
  }
}

/*
* 获取绝对位置X
* view:视图
*/
double clientX(FCView view) {
  if (view.isNotEmpty) {
    double cLeft = view.location.x;
    if (view.getParent().isNotEmpty) {
      FCView pView = view.getParent();
      if (pView.displayOffset && pView.scrollH != 0) {
        return cLeft + clientX(pView) - pView.scrollH;
      } else {
        return cLeft + clientX(pView);
      }
    } else {
      return cLeft;
    }
  } else {
    return 0;
  }
}

/*
* 获取绝对位置Y
* view:视图
*/
double clientY(FCView view) {
  if (view.isNotEmpty) {
    double cTop = view.location.y;
    if (view.getParent().isNotEmpty) {
      FCView pView = view.getParent();
      if (pView.displayOffset && pView.scrollV != 0) {
        return cTop + clientY(pView) - pView.scrollV;
      } else {
        return cTop + clientY(pView);
      }
    } else {
      return cTop;
    }
  } else {
    return 0;
  }
}

/*
* 是否包含坐标
* view:视图
* mp:坐标
*/
bool containsPoint(FCView view, FCPoint mp) {
  if(isViewEnabled(view)){
    double clx = clientX(view);
    double cly = clientY(view);
    FCSize size = view.size;
    FCPoint cp = FCPoint(mp.x - clx, mp.y - cly);
    if (cp.x >= 0 && cp.x <= size.cx && cp.y >= 0 && cp.y <= size.cy) {
      return true;
    } else {
      return false;
    }
  }else{
    return false;
  }
}

/*
* 查找有预处理事件的父视图
* view:视图
*/
FCView findPreviewsEventParent(FCView view) {
  if (!view.allowPreviewsEvent && view.getParent().isNotEmpty) {
    if (view.getParent().allowPreviewsEvent) {
      return view.getParent();
    } else {
      return findPreviewsEventParent(view.getParent());
    }
  } else {
    FCView noneView = FCView();
    noneView.isNotEmpty = false;
    return noneView;
  }
}

/*
* 根据名称查找视图
* name:名称
* views:视图集合
*/
FCView findViewByName(String name, List<FCView> views) {
  int size = views.length;
  for (int i = 0; i < size; i++) {
    FCView view = views[i];
    if (view.viewName == name) {
      return view;
    } else {
      if (view.views.isNotEmpty) {
        FCView subView = findViewByName(name, view.views);
        if (subView.isNotEmpty) {
          return subView;
        }
      }
    }
  }
  FCView noneView = FCView();
  noneView.isNotEmpty = false;
  return noneView;
}

/*
* 根据坐标查找视图
* mp:坐标
* views:视图集合
*/
FCView findView(FCPoint mp, List<FCView> views) {
  int size = views.length;
  for (int i = size - 1; i >= 0; i--) {
    FCView view = views[i];
    if (view.visible && view.topMost) {
      bool hasPoint = false;
      if (view.getPaint().hasContainsPointEvent) {
            hasPoint = view.getPaint().onContainsPoint(view, mp);
        } else {
            hasPoint = containsPoint(view, mp);
        }
      if (hasPoint) {
        if (view.vScrollIsVisible) {
          double clx = clientX(view);
          if (mp.x >= clx + view.size.cx - view.scrollSize) {
            return view;
          }
        }
        if (view.hScrollIsVisible) {
          double cly = clientY(view);
          if (mp.y >= cly + view.size.cy - view.scrollSize) {
            return view;
          }
        }
        if (view.views.isNotEmpty) {
          FCView subView = findView(mp, view.views);
          if (subView.isNotEmpty) {
            return subView;
          }
        }
        return view;
      }
    }
  }
  for (int i = size - 1; i >= 0; i--) {
    FCView view = views[i];
    if (view.visible && !view.topMost) {
      bool hasPoint = false;
      if (view.getPaint().hasContainsPointEvent) {
            hasPoint = view.getPaint().onContainsPoint(view, mp);
        } else {
            hasPoint = containsPoint(view, mp);
        }
      if (hasPoint) {
        if (view.vScrollIsVisible) {
          double clx = clientX(view);
          if (mp.x >= clx + view.size.cx - view.scrollSize) {
            return view;
          }
        }
        if (view.hScrollIsVisible) {
          double cly = clientY(view);
          if (mp.y >= cly + view.size.cy - view.scrollSize) {
            return view;
          }
        }
        if (view.views.isNotEmpty) {
          FCView subView = findView(mp, view.views);
          if (subView.isNotEmpty) {
            return subView;
          }
        }
        return view;
      }
    }
  }
  FCView noneView = FCView();
  noneView.isNotEmpty = false;
  return noneView;
}

/*
* 是否重绘时可见
* view:视图
*/
bool isPaintVisible(FCView view) {
  if (view.visible) {
    if (view.getParent().isNotEmpty) {
      FCView pView = view.getParent();
      if (pView.visible) {
        return isPaintVisible(pView);
      } else {
        return false;
      }
    } else {
      return true;
    }
  } else {
    return false;
  }
}

/*
* 是否可用
* view:视图
*/
bool isViewEnabled(FCView view) {
  if (view.enabled) {
    if (view.getParent().isNotEmpty) {
      FCView pView = view.getParent();
      if (pView.enabled) {
        return isViewEnabled(pView);
      } else {
        return false;
      }
    } else {
      return true;
    }
  } else {
    return false;
  }
}

/*
* 获取区域的交集
*/
int getIntersectRect(FCRect lpDestRect, FCRect lpSrc1Rect, FCRect lpSrc2Rect) {
  lpDestRect.left = max(lpSrc1Rect.left, lpSrc2Rect.left);
  lpDestRect.right = min(lpSrc1Rect.right, lpSrc2Rect.right);
  lpDestRect.top = max(lpSrc1Rect.top, lpSrc2Rect.top);
  lpDestRect.bottom = min(lpSrc1Rect.bottom, lpSrc2Rect.bottom);
  if (lpDestRect.right > lpDestRect.left &&
      lpDestRect.bottom > lpDestRect.top) {
    return 1;
  } else {
    lpDestRect.left = 0;
    lpDestRect.right = 0;
    lpDestRect.top = 0;
    lpDestRect.bottom = 0;
    return 0;
  }
}

/*
* 重绘视图
* views:视图集合
* paint:绘图对象
* rect:区域
*/
void renderViews(List<FCView> views, FCPaint paint, FCRect rect) {
  int viewsSize = views.length;
  for (int i = 0; i < viewsSize; i++) {
    FCView view = views[i];
    if (!rect.isNotEmpty) {
      FCRect noneRect = FCRect(0, 0, 0, 0);
      noneRect.isNotEmpty = false;
      if (view.views.isNotEmpty) {
        int subViewsSize = view.views.length;
        if (subViewsSize > 0) {
          renderViews(view.views, paint, noneRect);
        }
      }
      view.clipRect = noneRect;
      continue;
    }
    if (!view.topMost && isPaintVisible(view)) {
      double clx = clientX(view);
      double cly = clientY(view);
      FCRect drawRect = FCRect(0, 0, view.size.cx, view.size.cy);
      FCRect clipRect =
          FCRect(clx, cly, clx + view.size.cx, cly + view.size.cy);
      FCRect destRect = FCRect(0, 0, 0, 0);
      if (getIntersectRect(destRect, rect, clipRect) > 0) {
        paint.save();
        paint.setOffset(0, 0);
        paint.setClip(
            destRect.left, destRect.top, destRect.right, destRect.bottom);
        view.clipRect = destRect;
        paint.setOffset(clx, cly);
        if (paint.hasPaintEvent) {
          paint.onPaint(view, paint, drawRect);
        } else {
          onPaintDefault(view, paint, drawRect);
        }
        if (view.views.isNotEmpty) {
          int subViewsSize = view.views.length;
          if (subViewsSize > 0) {
            renderViews(view.views, paint, destRect);
          }
        }
        paint.setOffset(clx, cly);
        if (paint.hasPaintBorderEvent) {
          paint.onPaintBorder(view, paint, drawRect);
        } else {
          onPaintBorderDefault(view, paint, drawRect);
        }
        paint.restore();
      } else {
        FCRect noneRect = FCRect(0, 0, 0, 0);
        if (view.views.isNotEmpty) {
          int subViewsSize = view.views.length;
          if (subViewsSize > 0) {
            renderViews(view.views, paint, noneRect);
          }
        }
        view.clipRect = noneRect;
      }
    }
  }
  for (int i = 0; i < viewsSize; i++) {
    FCView view = views[i];
    if (!rect.isNotEmpty) {
      continue;
    }
    if (view.topMost && isPaintVisible(view)) {
      double clx = clientX(view);
      double cly = clientY(view);
      FCRect drawRect = FCRect(0, 0, view.size.cx, view.size.cy);
      FCRect clipRect =
          FCRect(clx, cly, clx + view.size.cx, cly + view.size.cy);
      FCRect destRect = FCRect(0, 0, 0, 0);
      if (getIntersectRect(destRect, rect, clipRect) > 0) {
        paint.save();
        paint.setOffset(0, 0);
        view.clipRect = destRect;
        paint.setClip(
            destRect.left, destRect.top, destRect.right, destRect.bottom);
        paint.setOffset(clx, cly);
        if (paint.hasPaintEvent) {
          paint.onPaint(view, paint, drawRect);
        } else {
          onPaintDefault(view, paint, drawRect);
        }
        if (view.views.isNotEmpty) {
          int subViewsSize = view.views.length;
          if (subViewsSize > 0) {
            renderViews(view.views, paint, destRect);
          }
        }
        paint.setOffset(clx, cly);
        if (paint.hasPaintBorderEvent) {
          paint.onPaintBorder(view, paint, drawRect);
        } else {
          onPaintBorderDefault(view, paint, drawRect);
        }
        paint.restore();
      } else {
        FCRect noneRect = FCRect(0, 0, 0, 0);
        if (view.views.isNotEmpty) {
          int subViewsSize = view.views.length;
          if (subViewsSize > 0) {
            renderViews(view.views, paint, noneRect);
          }
        }
        view.clipRect = noneRect;
      }
    }
  }
}

/*
* 全局刷新方法
* paint:绘图对象
*/
void invalidate(FCPaint paint)
{
  paint.isPainting = 1;
}

/*
* 刷新视图方法
* view:视图
*/
void invalidateView(FCView view)
 {
  FCPaint paint = view.getPaint();
  if(paint.isPainting == 2){
    paint.isPainting = 1;
  }else if(paint.isPainting == 0){
    paint.isPainting = 2;
    double clx = clientX(view);
    double cly = clientY(view);
    paint.paintRect = FCRect(clx * paint.scaleFactorX, cly * paint.scaleFactorY, (clx + view.size.cx) * paint.scaleFactorX, (cly + view.size.cy) * paint.scaleFactorY);
  }
 }

/*
* 更新悬浮状态
* views:视图集合
*/
void updateViewDefault(List<FCView> views) {
  for (int i = 0; i < views.length; i++) {
    FCView view = views[i];
    FCView pView = view.getParent();
    if (view.exAttributes.containsKey("leftstr")) {
      double pWidth = view.getPaint().size.cx / view.getPaint().scaleFactorX;
      if (pView.isNotEmpty) {
        pWidth = pView.size.cx;
      }
      String newStr = view.exAttributes["leftstr"] as String;
      newStr = newStr.replaceAll("%", "");
      view.location.x = double.parse(newStr) * pWidth / 100;
    }
    if (view.exAttributes.containsKey("topstr")) {
      double pHeight = view.getPaint().size.cy / view.getPaint().scaleFactorY;
      if (pView.isNotEmpty) {
        pHeight = pView.size.cy;
      }
      String newStr = view.exAttributes["topstr"] as String;
      newStr = newStr.replaceAll("%", "");
      view.location.y = double.parse(newStr) * pHeight / 100;
    }
    if (view.exAttributes.containsKey("widthstr")) {
      double pWidth = view.getPaint().size.cx / view.getPaint().scaleFactorX;
      if (pView.isNotEmpty) {
        pWidth = pView.size.cx;
      }
      String newStr = view.exAttributes["widthstr"] as String;
      newStr = newStr.replaceAll("%", "");
      view.size.cx = double.parse(newStr) * pWidth / 100;
    }
    if (view.exAttributes.containsKey("heightstr")) {
      double pHeight = view.getPaint().size.cy / view.getPaint().scaleFactorY;
      if (pView.isNotEmpty) {
        pHeight = pView.size.cy;
      }
      String newStr = view.exAttributes["heightstr"] as String;
      newStr = newStr.replaceAll("%", "");
      view.size.cy = double.parse(newStr) * pHeight / 100;
    }
    if (pView.isNotEmpty && pView.viewType != "split") {
      FCPadding margin = view.margin;
			FCPadding padding = pView.padding;
      if (view.dock == "fill") {
        view.location = FCPoint(margin.left + padding.left, margin.top + padding.top);
        double vcx = pView.size.cx - margin.left - padding.left - margin.right - padding.right;
				if (vcx < 0){
					vcx = 0;
				}
				double vcy = pView.size.cy - margin.top - padding.top - margin.bottom - padding.bottom;
				if (vcy < 0){
					vcy = 0;
				}
        view.size = FCSize(vcx, vcy);
      } else if (view.dock == "left") {
        view.location = FCPoint(margin.left + padding.left, margin.top + padding.top);
        double vcy = pView.size.cy - margin.top - padding.top - margin.bottom - padding.bottom;
				if (vcy < 0){
					vcy = 0;
				}
        view.size = FCSize(view.size.cx, vcy);
      } else if (view.dock == "top") {
        view.location = FCPoint(margin.left + padding.left, margin.top + padding.top);
        double vcx = pView.size.cx - margin.left - padding.left - margin.right - padding.right;
				if (vcx < 0){
					vcx = 0;
				}
        view.size = FCSize(vcx, view.size.cy);
      } else if (view.dock == "right") {
        view.location = FCPoint(pView.size.cx - view.size.cx - padding.right - margin.right, margin.top + padding.top);
        double vcy = pView.size.cy - margin.top - padding.top - margin.bottom - padding.bottom;
				if (vcy < 0){
					vcy = 0;
				}
        view.size = FCSize(view.size.cx, vcy);
      } else if (view.dock == "bottom") {
        view.location = FCPoint(margin.left + padding.left, pView.size.cy - view.size.cy - margin.bottom - padding.bottom);
        double vcx = pView.size.cx - margin.left - padding.left - margin.right - padding.right;
				if (vcx < 0){
					vcx = 0;
				}
        view.size = FCSize(vcx, view.size.cy);
      }
      if (view.align == "center") {
        view.location =
            FCPoint((pView.size.cx - view.size.cx) / 2, view.location.y);
      }else if(view.align == "right"){
        view.location =
            FCPoint((pView.size.cx - view.size.cx) / 2, view.location.y);
      }
      if (view.verticalAlign == "middle") {
        view.location =
            FCPoint(pView.size.cx - view.size.cx - padding.right - margin.right, view.location.y);
      }else if(view.verticalAlign == "bottom"){
        view.location =
            FCPoint(view.location.x, pView.size.cy - view.size.cy - padding.bottom - margin.bottom);
      }
    } else if (!pView.isNotEmpty) {
      if (view.dock == "fill") {
        view.size = FCSize(view.getPaint().size.cx / view.getPaint().scaleFactorX, view.getPaint().size.cy / view.getPaint().scaleFactorY);
      }
    }
    if (view.viewType == "split") {
      resetSplitLayoutDiv(view as FCSplitLayoutDiv);
    } else if (view.viewType == "tabview") {
      updateTabLayout(view as FCTabView);
    } else if (view.viewType == "layout") {
      resetLayoutDiv(view as FCLayoutDiv);
    } else if (view.viewType == "calendar") {
      updateCalendar(view as FCCalendar);
    }else if(view.viewType == "chart"){
        FCChart chart = view as FCChart;
        resetChartVisibleRecord(chart);
        checkChartLastVisibleIndex(chart);
        if (chart.getPaint().hasCalculateChartMaxMinEvent) {
            chart.getPaint().onCalculateChartMaxMin(chart);
        } else {
            calculateChartMaxMin(chart);
        }
    }
    if (view.views.isNotEmpty) {
      updateViewDefault(view.views);
    }
  }
}

/*
* 视图尺寸改变
*/
void windowResize(FCRect rect, int resizePoint, FCPoint nowPoint, FCPoint startTouchPoint){
	if (resizePoint == 0){
		rect.left = rect.left + nowPoint.x - startTouchPoint.x;
		rect.top = rect.top + nowPoint.y - startTouchPoint.y;
	}
	else if (resizePoint == 1){
		rect.left = rect.left + nowPoint.x - startTouchPoint.x;
		rect.bottom = rect.bottom + nowPoint.y - startTouchPoint.y;
	}
	else if (resizePoint == 2){
		rect.right = rect.right + nowPoint.x - startTouchPoint.x;
		rect.top = rect.top + nowPoint.y - startTouchPoint.y;
	}
	else if (resizePoint == 3){
		rect.right = rect.right + nowPoint.x - startTouchPoint.x;
		rect.bottom = rect.bottom + nowPoint.y - startTouchPoint.y;
	}
	else if (resizePoint == 4){
		rect.left = rect.left + nowPoint.x - startTouchPoint.x;
	}
	else if (resizePoint == 5){
		rect.top = rect.top + nowPoint.y - startTouchPoint.y;
	}
	else if (resizePoint == 6){
		rect.right = rect.right + nowPoint.x - startTouchPoint.x;
	}
	else if (resizePoint == 7){
		rect.bottom = rect.bottom + nowPoint.y - startTouchPoint.y;
	}
}

/*
* 获取调整尺寸的点
*/
int getResizeState(FCView view, FCPoint mp){
	double bWidth = 5;
	double width = view.size.cx, height = view.size.cy;
	if (mp.x >= 0 && mp.x <= bWidth * 2 && mp.y >= 0 && mp.y <= bWidth * 2){
		return 0;
	}else if (mp.x >= 0 && mp.x <= bWidth * 2 && mp.y >= height - bWidth * 2 && mp.y <= height){
		return 1;
	}else if (mp.x >= width - bWidth * 2 && mp.x <= width && mp.y >= 0 && mp.y <= bWidth * 2){
		return 2;
	}else if (mp.x >= width - bWidth * 2 && mp.x <= width && mp.y >= height - bWidth * 2 && mp.y <= height){
		return 3;
	}else if (mp.x >= 0 && mp.x <= bWidth && mp.y >= 0 && mp.y <= height){
		return 4;
	}else if (mp.x >= 0 && mp.x <= width && mp.y >= 0 && mp.y <= bWidth){
		return 5;
	}else if (mp.x >= width - bWidth && mp.x <= width && mp.y >= 0 && mp.y <= height){
		return 6;
	}else if (mp.x >= 0 && mp.x <= width && mp.y >= height - bWidth && mp.y <= height){
		return 7;
	}else{
		return -1;
	}
}

/*
* 获取格式化日期
*/
String getFormatDate(double num, String formatStr) {
  var date = DateTime.fromMillisecondsSinceEpoch(num.toInt());
  int year = date.year;
  int month = date.month;
  int day = date.day;
  int hour = date.hour;
  int minute = date.minute;
  int second = date.second;
  formatStr = formatStr.replaceAll("YYYY", year.toString());
  if (month < 10) {
    formatStr = formatStr.replaceAll("mm", "0" + month.toString());
  } else {
    formatStr = formatStr.replaceAll("mm", month.toString());
  }
  if (month < 10) {
    formatStr = formatStr.replaceAll("dd", "0" + day.toString());
  } else {
    formatStr = formatStr.replaceAll("dd", day.toString());
  }
  if (hour < 10) {
    formatStr = formatStr.replaceAll("HH", "0" + hour.toString());
  } else {
    formatStr = formatStr.replaceAll("HH", hour.toString());
  }
  if (minute < 10) {
    formatStr = formatStr.replaceAll("MM", "0" + minute.toString());
  } else {
    formatStr = formatStr.replaceAll("MM", minute.toString());
  }
  if (second < 10) {
    formatStr = formatStr.replaceAll("SS", "0" + second.toString());
  } else {
    formatStr = formatStr.replaceAll("SS", second.toString());
  }
  return formatStr;
}

/*
* 重绘复选按钮
* checkBox:视图
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawCheckBox(FCCheckBox checkBox, FCPaint paint, FCRect clipRect) {
    double width = checkBox.size.cx;
  double height = checkBox.size.cy;
  if (checkBox.textColor != "none") {
    double eRight = checkBox.buttonSize.cx + 10;
    FCRect eRect = FCRect(1, (height - checkBox.buttonSize.cy) / 2,
        checkBox.buttonSize.cx + 1, (height + checkBox.buttonSize.cy) / 2);
    if(!checkBox.text.isNotEmpty){
        eRect = FCRect((width - checkBox.buttonSize.cx) / 2, (height - checkBox.buttonSize.cy) / 2, (width + checkBox.buttonSize.cx) / 2, (height + checkBox.buttonSize.cy) / 2);
    }
    paint.drawRect(checkBox.textColor, 1, 0, eRect.left, eRect.top, eRect.right,
        eRect.bottom);
    if (checkBox.checked) {
      eRect.left += 2;
      eRect.top += 2;
      eRect.right -= 2;
      eRect.bottom -= 2;
      paint.beginPath();
			paint.addLine(eRect.left, eRect.top + 8, eRect.left + 6, eRect.bottom);
			paint.addLine(eRect.left + 6, eRect.bottom, eRect.right - 1, eRect.top);
			paint.drawPath(checkBox.textColor, 1, 0);
			paint.closePath();
    }
    if(checkBox.text.isNotEmpty){
        FCSize tSize = paint.textSize(checkBox.text, checkBox.font);
        paint.drawText(checkBox.text, checkBox.textColor, checkBox.font, eRight,
            (height - tSize.cy) / 2);
    }
  }
}

/*
* 重绘单选按钮
* radioButton:视图
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawRadioButton(
    FCRadioButton radioButton, FCPaint paint, FCRect clipRect) {
  double height = radioButton.size.cy;
  if (radioButton.textColor != "none") {
    double eRight = radioButton.buttonSize.cx + 10;
    FCRect eRect = FCRect(
        1,
        (height - radioButton.buttonSize.cy) / 2,
        radioButton.buttonSize.cx + 1,
        (height + radioButton.buttonSize.cy) / 2);
    paint.drawEllipse(radioButton.textColor, 1, 0, eRect.left, eRect.top,
        eRect.right, eRect.bottom);
    if (radioButton.checked) {
      eRect.left += 2;
      eRect.top += 2;
      eRect.right -= 2;
      eRect.bottom -= 2;
      paint.fillEllipse(radioButton.textColor, eRect.left, eRect.top,
          eRect.right, eRect.bottom);
    }
    FCSize tSize = paint.textSize(radioButton.text, radioButton.font);
    paint.drawText(radioButton.text, radioButton.textColor, radioButton.font,
        eRight, (height - tSize.cy) / 2);
  }
}

/*
* 点击复选按钮
* checkBox:视图
* mp:坐标
*/
void clickCheckBox(FCCheckBox checkBox, FCPoint mp) {
  checkBox.checked = !checkBox.checked;
}

/*
* 点击单选按钮
* radioButton:视图
* mp: 坐标
*/
void clickRadioButton(FCRadioButton radioButton, FCPoint mp) {
  if (radioButton.getParent().isNotEmpty &&
      radioButton.getParent().views.isNotEmpty) {
    for (int i = 0; i < radioButton.getParent().views.length; i++) {
      FCView rView = radioButton.getParent().views[i];
      if(rView.viewType == "radiobutton"){
        FCRadioButton rButton = rView as FCRadioButton;
        if (rButton != radioButton &&
            rButton.groupName == radioButton.groupName) {
          rButton.checked = false;
        }
      }
    }
  }
  radioButton.checked = true;
}

/*
* 重绘按钮
* button:视图
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawButton(FCView button, FCPaint paint, FCRect clipRect) {
  if (button == button.getPaint().getTouchDownView()) {
    if (button.pushedColor != "none") {
      paint.fillRoundRect(button.pushedColor, 0, 0, button.size.cx, button.size.cy, button.cornerRadius);
    } else {
      if (button.backColor != "none") {
        paint.fillRoundRect(button.backColor, 0, 0, button.size.cx, button.size.cy, button.cornerRadius);
      }
    }
  } else if (button == button.getPaint().getTouchMoveView()) {
    if (button.hoveredColor != "none") {
      paint.fillRoundRect(button.hoveredColor, 0, 0, button.size.cx, button.size.cy, button.cornerRadius);
    } else {
      if (button.backColor != "none") {
        paint.fillRoundRect(button.backColor, 0, 0, button.size.cx, button.size.cy, button.cornerRadius);
      }
    }
  } else if (button.backColor != "none") {
    paint.fillRoundRect(button.backColor, 0, 0, button.size.cx, button.size.cy, button.cornerRadius);
  }
  if (button.textColor != "none" && button.text.isNotEmpty) {
    FCSize tSize = paint.textSize(button.text, button.font);
    paint.drawText(button.text, button.textColor, button.font,
        (button.size.cx - tSize.cx) / 2, (button.size.cy - tSize.cy) / 2);
  }
  if (button.borderColor != "none") {
    paint.drawRoundRect(
        button.borderColor, button.borderWidth, 0, 0, 0, button.size.cx, button.size.cy, button.cornerRadius);
  }
}

/*
* 获取月的日数
* year:年
* month:月
*/
int getDaysInMonth(int year, int month) {
  switch (month) {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
      return 31;
    case 4:
    case 6:
    case 9:
    case 11:
      return 30;
    case 2:
      if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
        return 29;
      } else {
        return 28;
      }
  }
  return 0;
}

/*
* 根据字符获取月份
* month:月
*/
String getMonthStr(int month) {
  switch (month) {
    case 1:
      return "一月";
    case 2:
      return "二月";
    case 3:
      return "三月";
    case 4:
      return "四月";
    case 5:
      return "五月";
    case 6:
      return "六月";
    case 7:
      return "七月";
    case 8:
      return "八月";
    case 9:
      return "九月";
    case 10:
      return "十月";
    case 11:
      return "十一月";
    case 12:
      return "十二月";
    default:
      return "";
  }
}

/*
* 获取年
* years:年的集合
* year:年
*/
CYear getYear(Map<int, CYear> years, int year) {
  CYear cy = CYear();
  if (!years.containsKey(year)) {
    cy = CYear();
    cy.year = year;
    years[year] = cy;
    for (int i = 1; i <= 12; i++) {
      CMonth cMonth = CMonth();
      cMonth.year = year;
      cMonth.month = i;
      cy.months[i] = cMonth;
      int daysInMonth = getDaysInMonth(year, i);
      for (int j = 1; j <= daysInMonth; j++) {
        CDay cDay = CDay();
        cDay.year = year;
        cDay.month = i;
        cDay.day = j;
        cMonth.days[j] = cDay;
      }
    }
  } else {
    cy = years[year] as CYear;
  }
  return cy;
}

/*
* 显示隐藏日期层
* dayDiv:日期层
* visible:是否可见
*/
void showOrHideDayDiv(DayDiv dayDiv, bool visible) {
  int dayButtonSize = dayDiv.dayButtons.length;
  for (int i = 0; i < dayButtonSize; i++) {
    DayButton dayButton = dayDiv.dayButtons[i];
    dayButton.visible = visible;
  }
}

/*
* 显示隐藏月层
* monthDiv:月层
* visible:是否可见
*/
void showOrHideMonthDiv(MonthDiv monthDiv, bool visible) {
  int monthButtonSize = monthDiv.monthButtons.length;
  for (int i = 0; i < monthButtonSize; i++) {
    MonthButton monthButton = monthDiv.monthButtons[i];
    monthButton.visible = visible;
  }
}

/*
* 显示隐藏年层
* yearButtons:年层
* visible:是否可见
*/
void showOrHideYearDiv(YearDiv yearDiv, bool visible) {
  int yearButtonSize = yearDiv.yearButtons.length;
  for (int i = 0; i < yearButtonSize; i++) {
    YearButton yearButton = yearDiv.yearButtons[i];
    yearButton.visible = visible;
  }
}

/*
* 初始化日历
* calendar:日历
*/
void initCalendar(FCCalendar calendar) {
  calendar.setDayDiv(DayDiv());
  calendar.setMonthDiv(MonthDiv());
  calendar.setYearDiv(YearDiv());
  calendar.setHeadDiv(HeadDiv());
  calendar.setTimeDiv(TimeDiv());
  for (int i = 0; i < 42; i++) {
    DayButton dayButton = DayButton();
    calendar.getDayDiv().dayButtons.add(dayButton);
    DayButton dayFCButtonm = DayButton();
    dayFCButtonm.visible = false;
    calendar.getDayDiv().dayButtonsAm.add(dayFCButtonm);
  }
  for (int i = 0; i < 12; i++) {
    MonthButton monthButton = MonthButton();
    monthButton.month = (i + 1);
    calendar.getMonthDiv().monthButtons.add(monthButton);
    MonthButton monthButtonAm = MonthButton();
    monthButtonAm.visible = false;
    monthButtonAm.month = (i + 1);
    calendar.getMonthDiv().monthButtonsAm.add(monthButtonAm);
  }

  for (int i = 0; i < 12; i++) {
    YearButton yearButton = YearButton();
    calendar.getYearDiv().yearButtons.add(yearButton);
    YearButton yearButtonAm = YearButton();
    yearButtonAm.visible = false;
    calendar.getYearDiv().yearButtonsAm.add(yearButtonAm);
  }
}

/*
* 获取星期
* y:年
* m:月
* d:日
*/
int dayOfWeek(int y, int m, int d) {
  if (m == 1 || m == 2) {
    m += 12;
    y--;
  }
  return (((d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) + 1) %
          7)
      .toInt();
}

/*
* 获取当月
* calendar:日历
*/
CMonth getMonth(FCCalendar calendar) {
  return getYear(calendar.years, calendar.getSelectedDay().year)
      .months[calendar.getSelectedDay().month] as CMonth;
}

/*
* 获取下个月
* calendar:日历
* year:年
* month:月
*/
CMonth getNextMonth(FCCalendar calendar, int year, int month) {
  int nextMonth = month + 1;
  int nextYear = year;
  if (nextMonth == 13) {
    nextMonth = 1;
    nextYear += 1;
  }
  return getYear(calendar.years, nextYear).months[nextMonth] as CMonth;
}

/*
* 获取上个月
* calendar:日历
* year:年
* month:月
*/
CMonth getLastMonth(FCCalendar calendar, int year, int month) {
  int lastMonth = month - 1;
  int lastYear = year;
  if (lastMonth == 0) {
    lastMonth = 12;
    lastYear -= 1;
  }
  return getYear(calendar.years, lastYear).months[lastMonth] as CMonth;
}

/*
* 重置日期层布局
* calendar:日历
* dayDiv:日期层
* state:状态
*/
void resetDayDiv(FCCalendar calendar, DayDiv dayDiv, int state) {
  CMonth thisMonth = getMonth(calendar);
  CMonth lastMonth = getLastMonth(calendar, thisMonth.year, thisMonth.month);
  CMonth nextMonth = getNextMonth(calendar, thisMonth.year, thisMonth.month);
  double left = 0;
  double headHeight = calendar.getHeadDiv().bounds.bottom;
  double top = headHeight;
  double width = calendar.size.cx;
  double height = calendar.size.cy;
  height -=
      calendar.getTimeDiv().bounds.bottom - calendar.getTimeDiv().bounds.top;
  double dayButtonHeight = height - headHeight;
  if (dayButtonHeight < 1) {
    dayButtonHeight = 1;
  }
  double toY = 0;
  if (dayDiv.aDirection == 1) {
    toY = dayButtonHeight * dayDiv.aTick / dayDiv.aTotalTick;
    if (state == 1) {
      thisMonth = nextMonth;
      int month = thisMonth.month;
      lastMonth = getLastMonth(calendar, thisMonth.year, month);
      nextMonth = getNextMonth(calendar, thisMonth.year, month);
    }
  } else if (dayDiv.aDirection == 2) {
    toY = -dayButtonHeight * dayDiv.aTick / dayDiv.aTotalTick;
    if (state == 1) {
      thisMonth = lastMonth;
      int month = thisMonth.month;
      lastMonth = getLastMonth(calendar, thisMonth.year, month);
      nextMonth = getNextMonth(calendar, thisMonth.year, month);
    }
  }
  int buttonSize = 0;
  if (state == 0) {
    buttonSize = dayDiv.dayButtons.length;
  } else if (state == 1) {
    buttonSize = dayDiv.dayButtonsAm.length;
  }
  double dheight = dayButtonHeight / 6;
  Map<int, CDay> days = thisMonth.days;
  CDay firstDay = days[1] as CDay;
  int startDayOfWeek = dayOfWeek(firstDay.year, firstDay.month, firstDay.day);
  for (int i = 0; i < buttonSize; i++) {
    DayButton dayButton = DayButton();
    if (state == 0) {
      dayButton = dayDiv.dayButtons[i];
      buttonSize = dayDiv.dayButtons.length;
    } else if (state == 1) {
      dayButton = dayDiv.dayButtonsAm[i];
      buttonSize = dayDiv.dayButtonsAm.length;
    }
    if (i == 35) {
      dheight = height - top;
    }
    double vOffset = 0;
    if (state == 1) {
      if (dayDiv.aTick > 0) {
        dayButton.visible = true;
        if (dayDiv.aDirection == 1) {
          vOffset = toY - dayButtonHeight;
        } else if (dayDiv.aDirection == 2) {
          vOffset = toY + dayButtonHeight;
        }
      } else {
        dayButton.visible = false;
        continue;
      }
    } else {
      vOffset = toY;
    }
    if ((i + 1) % 7 == 0) {
      FCPoint dp = FCPoint(left, top + vOffset);
      FCSize ds = FCSize(width - left, dheight);
      FCRect bounds = FCRect(dp.x, dp.y, dp.x + ds.cx, dp.y + ds.cy);
      dayButton.bounds = bounds;
      left = 0;
      if (i != 0 && i != buttonSize - 1) {
        top += dheight;
      }
    } else {
      FCPoint dp = FCPoint(left, top + vOffset);
      FCSize ds = FCSize(width / 7 + ((i + 1) % 7) % 2, dheight);
      FCRect bounds = FCRect(dp.x, dp.y, dp.x + ds.cx, dp.y + ds.cy);
      dayButton.bounds = bounds;
      left += ds.cx;
    }
    CDay cDay = CDay();
    dayButton.inThisMonth = false;
    if (i >= startDayOfWeek && i <= startDayOfWeek + days.length - 1) {
      cDay = days[i - startDayOfWeek + 1] as CDay;
      dayButton.inThisMonth = true;
    } else if (i < startDayOfWeek) {
      cDay = lastMonth.days[lastMonth.days.length - startDayOfWeek + i + 1]
          as CDay;
    } else if (i > startDayOfWeek + days.length - 1) {
      cDay = nextMonth.days[i - startDayOfWeek - days.length + 1] as CDay;
    }
    dayButton.setDay(cDay);
    if (state == 0 &&
        dayButton.day.isNotEmpty &&
        dayButton.getDay() == calendar.getSelectedDay()) {
      dayButton.selected = true;
    } else {
      dayButton.selected = false;
    }
  }
}

/*
* 重置月层布局
* calendar:日历
* monthDiv:月层
* state:状态
*/
void resetMonthDiv(FCCalendar calendar, MonthDiv monthDiv, int state) {
  int thisYear = monthDiv.year;
  int lastYear = monthDiv.year - 1;
  int nextYear = monthDiv.year + 1;
  double left = 0;
  double headHeight = calendar.getHeadDiv().bounds.bottom;
  double top = headHeight;
  double width = calendar.size.cx;
  double height = calendar.size.cy;
  height -=
      calendar.getTimeDiv().bounds.bottom - calendar.getTimeDiv().bounds.top;
  double monthButtonHeight = height - top;
  if (monthButtonHeight < 1) {
    monthButtonHeight = 1;
  }
  double toY = 0;
  List<MonthButton> monthButtons = <MonthButton>[];
  if (monthDiv.aDirection == 1) {
    toY = monthButtonHeight * monthDiv.aTick / monthDiv.aTotalTick;
    if (state == 1) {
      thisYear = nextYear;
      lastYear = thisYear - 1;
      nextYear = thisYear + 1;
    }
  } else if (monthDiv.aDirection == 2) {
    toY = -monthButtonHeight * monthDiv.aTick / monthDiv.aTotalTick;
    if (state == 1) {
      thisYear = lastYear;
      lastYear = thisYear - 1;
      nextYear = thisYear + 1;
    }
  }
  if (state == 0) {
    monthButtons = monthDiv.monthButtons;
  } else if (state == 1) {
    monthButtons = monthDiv.monthButtonsAm;
  }
  double dheight = monthButtonHeight / 3;
  int buttonSize = monthButtons.length;
  for (int i = 0; i < buttonSize; i++) {
    if (i == 8) {
      dheight = height - top;
    }
    MonthButton monthButton = monthButtons[i];
    monthButton.year = thisYear;
    double vOffSet = 0;
    if (state == 1) {
      if (monthDiv.aTick > 0) {
        monthButton.visible = true;
        if (monthDiv.aDirection == 1) {
          vOffSet = toY - monthButtonHeight;
        } else if (monthDiv.aDirection == 2) {
          vOffSet = toY + monthButtonHeight;
        }
      } else {
        monthButton.visible = false;
        continue;
      }
    } else {
      vOffSet = toY;
    }
    if ((i + 1) % 4 == 0) {
      FCPoint dp = FCPoint(left, top + vOffSet);
      FCSize ds = FCSize(width - left, dheight);
      FCRect bounds = FCRect(dp.x, dp.y, dp.x + ds.cx, dp.y + ds.cy);
      monthButton.bounds = bounds;
      left = 0;
      if (i != 0 && i != buttonSize - 1) {
        top += dheight;
      }
    } else {
      FCPoint dp = FCPoint(left, top + vOffSet);
      FCSize ds = FCSize(width / 4 + ((i + 1) % 4) % 2, dheight);
      FCRect bounds = FCRect(dp.x, dp.y, dp.x + ds.cx, dp.y + ds.cy);
      monthButton.bounds = bounds;
      left += ds.cx;
    }
  }
}

/*
* 重置年层布局
* calendar:日历
* yearDiv:年层
* state:状态
*/
void resetYearDiv(FCCalendar calendar, YearDiv yearDiv, int state) {
  int thisStartYear = yearDiv.startYear;
  int lastStartYear = yearDiv.startYear - 12;
  int nextStartYear = yearDiv.startYear + 12;
  double left = 0;
  double headHeight = calendar.getHeadDiv().bounds.bottom;
  double top = headHeight;
  double width = calendar.size.cx;
  double height = calendar.size.cy;
  height -=
      calendar.getTimeDiv().bounds.bottom - calendar.getTimeDiv().bounds.top;
  double yearButtonHeight = height - top;
  if (yearButtonHeight < 1) {
    yearButtonHeight = 1;
  }
  double toY = 0;
  List<YearButton> yearButtons = <YearButton>[];
  if (yearDiv.aDirection == 1) {
    toY = yearButtonHeight * yearDiv.aTick / yearDiv.aTotalTick;
    if (state == 1) {
      thisStartYear = nextStartYear;
      lastStartYear = thisStartYear - 12;
      nextStartYear = thisStartYear + 12;
    }
  } else if (yearDiv.aDirection == 2) {
    toY = -yearButtonHeight * yearDiv.aTick / yearDiv.aTotalTick;
    if (state == 1) {
      thisStartYear = lastStartYear;
      lastStartYear = thisStartYear - 12;
      nextStartYear = thisStartYear + 12;
    }
  }
  if (state == 0) {
    yearButtons = yearDiv.yearButtons;
  } else if (state == 1) {
    yearButtons = yearDiv.yearButtonsAm;
  }
  double dheight = yearButtonHeight / 3;
  int buttonSize = yearDiv.yearButtons.length;
  for (int i = 0; i < buttonSize; i++) {
    if (i == 8) {
      dheight = height - top;
    }
    YearButton yearButton = yearButtons[i];
    yearButton.year = thisStartYear + i;
    double vOffSet = 0;
    if (state == 1) {
      if (yearDiv.aTick > 0) {
        yearButton.visible = true;
        if (yearDiv.aDirection == 1) {
          vOffSet = toY - yearButtonHeight;
        } else if (yearDiv.aDirection == 2) {
          vOffSet = toY + yearButtonHeight;
        }
      } else {
        yearButton.visible = false;
        continue;
      }
    } else {
      vOffSet = toY;
    }
    if ((i + 1) % 4 == 0) {
      FCPoint dp = FCPoint(left, top + vOffSet);
      FCSize ds = FCSize(width - left, dheight);
      FCRect bounds = FCRect(dp.x, dp.y, dp.x + ds.cx, dp.y + ds.cy);
      yearButton.bounds = bounds;
      left = 0;
      if (i != 0 && i != buttonSize - 1) {
        top += dheight;
      }
    } else {
      FCPoint dp = FCPoint(left, top + vOffSet);
      FCSize ds = FCSize(width / 4 + ((i + 1) % 4) % 2, dheight);
      FCRect bounds = FCRect(dp.x, dp.y, dp.x + ds.cx, dp.y + ds.cy);
      yearButton.bounds = bounds;
      left += ds.cx;
    }
  }
}

/*
* 选择开始年份
* calendar:日历
* yearDiv:年层
* startYear:开始年
*/
void selectStartYear(FCCalendar calendar, YearDiv yearDiv, int startYear) {
  if (yearDiv.startYear != startYear) {
    if (startYear > yearDiv.startYear) {
      yearDiv.aDirection = 1;
    } else {
      yearDiv.aDirection = 2;
    }
    if (calendar.useAnimation) {
      yearDiv.aTick = yearDiv.aTotalTick;
    }
    yearDiv.startYear = startYear;
  }
}

/*
* 选择年份
* calendar:日历
* monthDiv:月层
* year:年
*/
void selectYear(FCCalendar calendar, MonthDiv monthDiv, int year) {
  if (monthDiv.year != year) {
    if (year > monthDiv.year) {
      monthDiv.aDirection = 1;
    } else {
      monthDiv.aDirection = 2;
    }
    if (calendar.useAnimation) {
      monthDiv.aTick = monthDiv.aTotalTick;
    }
    monthDiv.year = year;
  }
}

/*
* 选中日期
* calendar:日历
* dayDiv:日期层
* selectedDay:选中日
* lastDay:上一日
*/
void selectDay(
    FCCalendar calendar, DayDiv dayDiv, CDay selectedDay, CDay lastDay) {
  CMonth m = getYear(calendar.years, selectedDay.year).months[selectedDay.month]
      as CMonth;
  CMonth thisMonth =
      getYear(calendar.years, lastDay.year).months[lastDay.month] as CMonth;
  if (m != thisMonth) {
    if (thisMonth.year * 12 + thisMonth.month > m.year * 12 + m.month) {
      dayDiv.aDirection = 2;
    } else {
      dayDiv.aDirection = 1;
    }
    int i = 0;
    int buttonSize = dayDiv.dayButtons.length;
    for (i = 0; i < buttonSize; i++) {
      DayButton dayButton = dayDiv.dayButtons[i];
      if ((dayDiv.aDirection == 1 && dayButton.getDay() == thisMonth.days[0]) ||
          (dayDiv.aDirection == 2 &&
              dayButton.getDay() ==
                  thisMonth.days[thisMonth.days.length - 1])) {
        dayDiv.aClickRowFrom = i ~/ 7;
        if (i % 7 != 0) {
          dayDiv.aClickRowFrom += 1;
        }
      }
    }
    resetDayDiv(calendar, dayDiv, 0);
    buttonSize = dayDiv.dayButtonsAm.length;
    for (i = 0; i < buttonSize; i++) {
      DayButton dayFCButtonm = dayDiv.dayButtonsAm[i];
      if ((dayDiv.aDirection == 1 && dayFCButtonm.getDay() == m.days[0]) ||
          (dayDiv.aDirection == 2 &&
              dayFCButtonm.getDay() == m.days[m.days.length - 1])) {
        dayDiv.aClickRowTo = i ~/ 7;
        if (i % 7 != 0) {
          dayDiv.aClickRowTo += 1;
        }
      }
    }

    if (calendar.useAnimation) {
      dayDiv.aTick = dayDiv.aTotalTick;
    }
  } else {
    int dayButtonsSize = dayDiv.dayButtons.length;
    for (int i = 0; i < dayButtonsSize; i++) {
      DayButton dayButton = dayDiv.dayButtons[i];
      if (dayButton.getDay() != selectedDay) {
        dayButton.selected = false;
      }
    }
  }
}

/*
* 日历的秒表
* calendar:日历
*/
void calendarTimer(FCCalendar calendar) {
  bool paint = false;
  if (calendar.getDayDiv().aTick > 0) {
    calendar.getDayDiv().aTick = calendar.getDayDiv().aTick * 2 ~/ 3;
    paint = true;
  }
  if (calendar.getMonthDiv().aTick > 0) {
    calendar.getMonthDiv().aTick = calendar.getMonthDiv().aTick * 2 ~/ 3;
    paint = true;
  }
  if (calendar.getYearDiv().aTick > 0) {
    calendar.getYearDiv().aTick = calendar.getYearDiv().aTick * 2 ~/ 3;
    paint = true;
  }
  if (paint) {
    updateCalendar(calendar);
    invalidateView(calendar);
  }
}

/*
* 更新日历的布局
* calendar:日历
*/
void updateCalendar(FCCalendar calendar) {
  calendar.getHeadDiv().bounds = FCRect(0, 0, calendar.size.cx, 80);
  if (calendar.mode == "day") {
    resetDayDiv(calendar, calendar.getDayDiv(), 0);
    resetDayDiv(calendar, calendar.getDayDiv(), 1);
  } else if (calendar.mode == "month") {
    resetMonthDiv(calendar, calendar.getMonthDiv(), 0);
    resetMonthDiv(calendar, calendar.getMonthDiv(), 1);
  } else if (calendar.mode == "year") {
    resetYearDiv(calendar, calendar.getYearDiv(), 0);
    resetYearDiv(calendar, calendar.getYearDiv(), 1);
  }
}

/*
* 绘制头部层
* calendar:日历
* headDiv:头部层
* paint:绘图对象
*/
void drawHeadDiv(FCCalendar calendar, HeadDiv headDiv, FCPaint paint) {
  FCRect bounds = headDiv.bounds;
  if (headDiv.backColor != "none") {
    paint.fillRect(headDiv.backColor, bounds.left, bounds.top, bounds.right,
        bounds.bottom);
  }
  List<String> weekStrings = <String>[];
  weekStrings.add("周日");
  weekStrings.add("周一");
  weekStrings.add("周二");
  weekStrings.add("周三");
  weekStrings.add("周四");
  weekStrings.add("周五");
  weekStrings.add("周六");
  double w = bounds.right - bounds.left;
  double left = bounds.left;
  for (int i = 0; i < 7; i++) {
    FCSize weekDaySize = paint.textSize(weekStrings[i], headDiv.weekFont);
    double textX = left + (w / 7) / 2 - weekDaySize.cx / 2;
    double textY = bounds.bottom - weekDaySize.cy - 2;
    paint.drawText(
        weekStrings[i], headDiv.textColor, headDiv.weekFont, textX, textY);
    left += w / 7;
  }
  String drawTitle = "";
  if (calendar.mode == "day") {
    drawTitle = calendar.getSelectedDay().year.toString() +
        "年".toString() +
        calendar.getSelectedDay().month.toString() +
        "月".toString();
  } else if (calendar.mode == "month") {
    drawTitle = calendar.getMonthDiv().year.toString() + "年".toString();
  } else {
    drawTitle = calendar.getYearDiv().startYear.toString() +
        "年-".toString() +
        (calendar.getYearDiv().startYear + 11).toString() +
        "年".toString();
  }
  FCSize tSize = paint.textSize(drawTitle, headDiv.titleFont);
  paint.drawText(drawTitle, headDiv.textColor, headDiv.titleFont,
      bounds.left + (w - tSize.cx) / 2, 30);
  double tR = 10;
  //画左右三角
  paint.beginPath();
  paint.addLine(5, bounds.top + (bounds.bottom - bounds.top) / 2, 5 + tR * 2,
      bounds.top + (bounds.bottom - bounds.top) / 2 - tR);
  paint.addLine(5 + tR * 2, bounds.top + (bounds.bottom - bounds.top) / 2 - tR,
      5 + tR * 2, bounds.top + (bounds.bottom - bounds.top) / 2 + tR);
  paint.addLine(5 + tR * 2, bounds.top + (bounds.bottom - bounds.top) / 2 + tR,
      5, bounds.top + (bounds.bottom - bounds.top) / 2);
  paint.fillPath(headDiv.arrowColor);
  paint.closePath();

  paint.beginPath();
  paint.addLine(
      bounds.right - 5,
      bounds.top + (bounds.bottom - bounds.top) / 2,
      bounds.right - 5 - tR * 2,
      bounds.top + (bounds.bottom - bounds.top) / 2 - tR);
  paint.addLine(
      bounds.right - 5 - tR * 2,
      bounds.top + (bounds.bottom - bounds.top) / 2 - tR,
      bounds.right - 5 - tR * 2,
      bounds.top + (bounds.bottom - bounds.top) / 2 + tR);
  paint.addLine(
      bounds.right - 5 - tR * 2,
      bounds.top + (bounds.bottom - bounds.top) / 2 + tR,
      bounds.right - 5,
      bounds.top + (bounds.bottom - bounds.top) / 2);
  paint.fillPath(headDiv.arrowColor);
  paint.closePath();
}

/*
* 绘制日的按钮
* calendar:日历
* dayButton:日期按钮
* paint:绘图对象
*/
void drawDayButton(FCCalendar calendar, DayButton dayButton, FCPaint paint) {
  if (dayButton.day.isNotEmpty) {
    FCRect bounds = dayButton.bounds;
    String text = dayButton.getDay().day.toString();
    if (dayButton.backColor != "none") {
      paint.fillRect(dayButton.backColor, bounds.left + 2, bounds.top + 2,
          bounds.right - 2, bounds.bottom - 2);
    }
    if (dayButton.inThisMonth) {
      paint.drawText(text, dayButton.textColor, dayButton.font, bounds.left + 5,
          bounds.top + 7);
    } else {
      paint.drawText(text, dayButton.textColor2, dayButton.font,
          bounds.left + 5, bounds.top + 7);
    }
    if (dayButton.borderColor != "none") {
      paint.drawRect(dayButton.borderColor, 1, 0, bounds.left + 2,
          bounds.top + 2, bounds.right - 2, bounds.bottom - 2);
    }
  }
}

/*
* 绘制月的按钮
* calendar:日历
* monthButton:月按钮
* paint:绘图对象
*/
void drawMonthButton(
    FCCalendar calendar, MonthButton monthButton, FCPaint paint) {
  FCRect bounds = monthButton.bounds;
  String text = getMonthStr(monthButton.month);
  if (monthButton.backColor != "none") {
    paint.fillRect(monthButton.backColor, bounds.left + 2, bounds.top + 2,
        bounds.right - 2, bounds.bottom - 2);
  }
  paint.drawText(text, monthButton.textColor, monthButton.font, bounds.left + 5,
      bounds.top + 7);
  if (monthButton.borderColor != "none") {
    paint.drawRect(monthButton.borderColor, 1, 0, bounds.left + 2,
        bounds.top + 2, bounds.right - 2, bounds.bottom - 2);
  }
}

/*
* 绘制年的按钮
* calendar:日历
* yearButton:年按钮
* paint:绘图对象
*/
void drawYearButton(FCCalendar calendar, YearButton yearButton, FCPaint paint) {
  FCRect bounds = yearButton.bounds;
  String text = yearButton.year.toString();
  if (yearButton.backColor != "none") {
    paint.fillRect(yearButton.backColor, bounds.left + 2, bounds.top + 2,
        bounds.right - 2, bounds.bottom - 2);
  }
  paint.drawText(text, yearButton.textColor, yearButton.font, bounds.left + 5,
      bounds.top + 7);
  if (yearButton.borderColor != "none") {
    paint.drawRect(yearButton.borderColor, 1, 0, bounds.left + 2,
        bounds.top + 2, bounds.right - 2, bounds.bottom - 2);
  }
}

/*
* 绘制日历
* calendar:日历
* paint:绘图对象
*/
void drawCalendar(FCCalendar calendar, FCPaint paint) {
  if (calendar.backColor != "none") {
    paint.fillRect(
        calendar.backColor, 0, 0, calendar.size.cx, calendar.size.cy);
  }
  if (calendar.mode == "day") {
    int dayButtonsSize = calendar.getDayDiv().dayButtons.length;
    for (int i = 0; i < dayButtonsSize; i++) {
      DayButton dayButton = calendar.getDayDiv().dayButtons[i];
      if (dayButton.visible) {
        if (paint.hasPaintCalendarDayButtonEvent) {
          paint.onPaintCalendarDayButton(calendar, dayButton, paint);
        } else {
          drawDayButton(calendar, dayButton, paint);
        }
      }
    }
    int dayFCButtonmSize = calendar.getDayDiv().dayButtonsAm.length;
    for (int i = 0; i < dayFCButtonmSize; i++) {
      DayButton dayButton = calendar.getDayDiv().dayButtonsAm[i];
      if (dayButton.visible) {
        if (paint.hasPaintCalendarDayButtonEvent) {
          paint.onPaintCalendarDayButton(calendar, dayButton, paint);
        } else {
          drawDayButton(calendar, dayButton, paint);
        }
      }
    }
  } else if (calendar.mode == "month") {
    int monthButtonsSize = calendar.getMonthDiv().monthButtons.length;
    for (int i = 0; i < monthButtonsSize; i++) {
      MonthButton monthButton = calendar.getMonthDiv().monthButtons[i];
      if (monthButton.visible) {
        if (paint.hasPaintCalendarMonthButtonEvent) {
          paint.onPaintCalendarMonthButton(calendar, monthButton, paint);
        } else {
          drawMonthButton(calendar, monthButton, paint);
        }
      }
    }
    int monthFCButtonmSize = calendar.getMonthDiv().monthButtonsAm.length;
    for (int i = 0; i < monthFCButtonmSize; i++) {
      MonthButton monthButton = calendar.getMonthDiv().monthButtonsAm[i];
      if (monthButton.visible) {
        if (paint.hasPaintCalendarMonthButtonEvent) {
          paint.onPaintCalendarMonthButton(calendar, monthButton, paint);
        } else {
          drawMonthButton(calendar, monthButton, paint);
        }
      }
    }
  } else if (calendar.mode == "year") {
    int yearButtonsSize = calendar.getYearDiv().yearButtons.length;
    for (int i = 0; i < yearButtonsSize; i++) {
      YearButton yearButton = calendar.getYearDiv().yearButtons[i];
      if (yearButton.visible) {
        if (paint.hasPaintCalendarYearButtonEvent) {
          paint.onPaintCalendarYearButton(calendar, yearButton, paint);
        } else {
          drawYearButton(calendar, yearButton, paint);
        }
      }
    }
    int yearFCButtonmSize = calendar.getYearDiv().yearButtonsAm.length;
    for (int i = 0; i < yearFCButtonmSize; i++) {
      YearButton yearButton = calendar.getYearDiv().yearButtonsAm[i];
      if (yearButton.visible) {
        if (paint.hasPaintCalendarYearButtonEvent) {
          paint.onPaintCalendarYearButton(calendar, yearButton, paint);
        } else {
          drawYearButton(calendar, yearButton, paint);
        }
      }
    }
  }
  if (paint.hasPaintHeadDivEvent) {
    paint.onPaintHeadDiv(calendar, calendar.getHeadDiv(), paint);
  } else {
    drawHeadDiv(calendar, calendar.getHeadDiv(), paint);
  }
  if (calendar.borderColor != "none") {
    paint.drawRect(
        calendar.borderColor, 1, 0, 0, 0, calendar.size.cx, calendar.size.cy);
  }
}

/*
* 点击日的按钮
* calendar:日历
* dayButton:日期按钮
* mp:坐标
*/
void clickDayButton(FCCalendar calendar, DayButton dayButton, FCPoint mp) {
  CDay lastDay = calendar.getSelectedDay();
  calendar.setSelectedDay(dayButton.getDay());
  selectDay(calendar, calendar.getDayDiv(), calendar.getSelectedDay(), lastDay);
  updateCalendar(calendar);
  invalidateView(calendar);
}

/*
* 点击月的按钮
* calendar:日历
* monthButton:月按钮
* mp:坐标
*/
void clickMonthButton(
    FCCalendar calendar, MonthButton monthButton, FCPoint mp) {
  CMonth month = getYear(calendar.years, monthButton.year)
      .months[monthButton.month] as CMonth;
  calendar.mode = "day";
  CDay lastDay = calendar.getSelectedDay();
  calendar.setSelectedDay(month.days[1] as CDay);
  selectDay(calendar, calendar.getDayDiv(), calendar.getSelectedDay(), lastDay);
  updateCalendar(calendar);
  invalidateView(calendar);
}

/*
* 点击年的按钮
* calendar:日历
* yearButton:年按钮
* mp:坐标
*/
void clickYearButton(FCCalendar calendar, YearButton yearButton, FCPoint mp) {
  calendar.mode = "month";
  selectYear(calendar, calendar.getMonthDiv(), yearButton.year);
  updateCalendar(calendar);
  invalidateView(calendar);
}

/*
* 点击左侧的按钮
* calendar:日历
* headDiv:头部层
* mp:坐标
*/
void clickLastButton(FCCalendar calendar, HeadDiv headDiv, FCPoint mp) {
  if (calendar.mode == "day") {
    CMonth lastMonth = getLastMonth(calendar, calendar.getSelectedDay().year,
        calendar.getSelectedDay().month);
    CDay lastDay = calendar.getSelectedDay();
    calendar.setSelectedDay(lastMonth.days[1] as CDay);
    selectDay(
        calendar, calendar.getDayDiv(), calendar.getSelectedDay(), lastDay);
    updateCalendar(calendar);
    invalidateView(calendar);
  } else if (calendar.mode == "month") {
    int year = calendar.getMonthDiv().year;
    year -= 1;
    selectYear(calendar, calendar.getMonthDiv(), year);
    updateCalendar(calendar);
    invalidateView(calendar);
  } else if (calendar.mode == "year") {
    int year = calendar.getYearDiv().startYear;
    year -= 12;
    selectStartYear(calendar, calendar.getYearDiv(), year);
    updateCalendar(calendar);
    invalidateView(calendar);
  }
}

/*
* 点击右侧的按钮
* calendar:日历
* headDiv:头部层
* mp:坐标
*/
void clickNextButton(FCCalendar calendar, HeadDiv headDiv, FCPoint mp) {
  if (calendar.mode == "day") {
    CMonth nextMonth = getNextMonth(calendar, calendar.getSelectedDay().year,
        calendar.getSelectedDay().month);
    CDay lastDay = calendar.getSelectedDay();
    calendar.setSelectedDay(nextMonth.days[1] as CDay);
    selectDay(
        calendar, calendar.getDayDiv(), calendar.getSelectedDay(), lastDay);
    updateCalendar(calendar);
    invalidateView(calendar);
  } else if (calendar.mode == "month") {
    int year = calendar.getMonthDiv().year;
    year += 1;
    selectYear(calendar, calendar.getMonthDiv(), year);
    updateCalendar(calendar);
    invalidateView(calendar);
  } else if (calendar.mode == "year") {
    int year = calendar.getYearDiv().startYear;
    year += 12;
    selectStartYear(calendar, calendar.getYearDiv(), year);
    updateCalendar(calendar);
    invalidateView(calendar);
  }
}

/*
* 改变模式的按钮
* calendar:日历
* headDiv:头部层
* mp:坐标
*/
void clickModeButton(FCCalendar calendar, HeadDiv headDiv, FCPoint mp) {
  if (calendar.mode == "day") {
    calendar.mode = "month";
    calendar.getMonthDiv().month = calendar.getSelectedDay().month;
    calendar.getMonthDiv().year = calendar.getSelectedDay().year;
    updateCalendar(calendar);
    invalidateView(calendar);
  } else if (calendar.mode == "month") {
    calendar.mode = "year";
    selectStartYear(
        calendar, calendar.getYearDiv(), calendar.getMonthDiv().year);
    updateCalendar(calendar);
    invalidateView(calendar);
  }
}

/*
* 点击日历
* calendar:日历
* mp:坐标
*/
void clickCalendar(FCCalendar calendar, FCPoint mp) {
  FCRect headBounds = calendar.getHeadDiv().bounds;
  if (mp.x >= headBounds.left &&
      mp.x <= headBounds.right &&
      mp.y >= headBounds.top &&
      mp.y <= headBounds.bottom) {
    double tR = 10;
    if (mp.x < headBounds.left + tR * 3) {
      clickLastButton(calendar, calendar.getHeadDiv(), mp);
      return;
    } else if (mp.x > headBounds.right - tR * 3) {
      clickNextButton(calendar, calendar.getHeadDiv(), mp);
      return;
    } else {
      clickModeButton(calendar, calendar.getHeadDiv(), mp);
      return;
    }
  }
  if (calendar.mode == "day") {
    int dayButtonsSize = calendar.getDayDiv().dayButtons.length;
    for (int i = 0; i < dayButtonsSize; i++) {
      DayButton dayButton = calendar.getDayDiv().dayButtons[i];
      if (dayButton.visible) {
        FCRect bounds = dayButton.bounds;
        if (mp.x >= bounds.left &&
            mp.x <= bounds.right &&
            mp.y >= bounds.top &&
            mp.y <= bounds.bottom) {
          clickDayButton(calendar, dayButton, mp);
          return;
        }
      }
    }
  } else if (calendar.mode == "month") {
    int monthButtonsSize = calendar.getMonthDiv().monthButtons.length;
    for (int i = 0; i < monthButtonsSize; i++) {
      MonthButton monthButton = calendar.getMonthDiv().monthButtons[i];
      if (monthButton.visible) {
        FCRect bounds = monthButton.bounds;
        if (mp.x >= bounds.left &&
            mp.x <= bounds.right &&
            mp.y >= bounds.top &&
            mp.y <= bounds.bottom) {
          clickMonthButton(calendar, monthButton, mp);
          return;
        }
      }
    }
  } else if (calendar.mode == "year") {
    int yearButtonsSize = calendar.getYearDiv().yearButtons.length;
    for (int i = 0; i < yearButtonsSize; i++) {
      YearButton yearButton = calendar.getYearDiv().yearButtons[i];
      if (yearButton.visible) {
        FCRect bounds = yearButton.bounds;
        if (mp.x >= bounds.left &&
            mp.x <= bounds.right &&
            mp.y >= bounds.top &&
            mp.y <= bounds.bottom) {
          clickYearButton(calendar, yearButton, mp);
          return;
        }
      }
    }
  }
}

/*
* 绘制滚动条
* div:图层
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawDivScrollBar(FCDiv div, FCPaint paint, FCRect clipRect) {
  div.hScrollIsVisible = false;
  div.vScrollIsVisible = false;
  //绘制滚动条
  if (div.showHScrollBar) {
    double contentWidth = getDivContentWidth(div);
    if (contentWidth > 0 && contentWidth > div.size.cx) {
      double sLeft = div.scrollH / contentWidth * div.size.cx;
      double sRight = (div.scrollH + div.size.cx) / contentWidth * div.size.cx;
      if (sRight - sLeft < div.scrollSize) {
        sRight = sLeft + div.scrollSize;
      }
      if (paint.getTouchMoveView() == div &&
          (div.hoverScrollHButton || div.downScrollHButton)) {
        paint.fillRect(div.scrollBarHoveredColor, sLeft,
            div.size.cy - div.scrollSize, sRight, div.size.cy);
      } else {
        paint.fillRect(div.scrollBarColor, sLeft, div.size.cy - div.scrollSize,
            sRight, div.size.cy);
      }
      div.hScrollIsVisible = true;
    }
  }
  if (div.showVScrollBar) {
    double contentHeight = getDivContentHeight(div);
    if (contentHeight > 0 && contentHeight > div.size.cy) {
      double sTop = div.scrollV / contentHeight * div.size.cy;
      double sBottom = sTop + (div.size.cy / contentHeight * div.size.cy);
      if (sBottom - sTop < div.scrollSize) {
        sBottom = sTop + div.scrollSize;
      }
      if (paint.getTouchMoveView() == div &&
          (div.hoverScrollVButton || div.downScrollVButton)) {
        paint.fillRect(div.scrollBarHoveredColor, div.size.cx - div.scrollSize,
            sTop, div.size.cx, sBottom);
      } else {
        paint.fillRect(div.scrollBarColor, div.size.cx - div.scrollSize, sTop,
            div.size.cx, sBottom);
      }
      div.vScrollIsVisible = true;
    }
  }
}

/*
* 获取内容的宽度
* div:图层
*/
double getDivContentWidth(FCView div) {
  double cWidth = 0;
  if (div.views.isNotEmpty) {
    for (int i = 0; i < div.views.length; i++) {
      if (div.views[i].visible) {
        if (cWidth < div.views[i].location.x + div.views[i].size.cx) {
          cWidth = div.views[i].location.x + div.views[i].size.cx;
        }
      }
    }
  }
  return cWidth;
}

/*
* 获取内容的高度
* div:图层
*/
double getDivContentHeight(FCView div) {
  double cHeight = 0;
  if (div.views.isNotEmpty) {
    for (int i = 0; i < div.views.length; i++) {
      if (div.views[i].visible) {
        if (cHeight < div.views[i].location.y + div.views[i].size.cy) {
          cHeight = div.views[i].location.y + div.views[i].size.cy;
        }
      }
    }
  }
  return cHeight;
}

/*
* 图层的鼠标移动方法
* div: 图层
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
*/
void touchMoveDiv(FCDiv div, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint) {
  div.hoverScrollHButton = false;
  div.hoverScrollVButton = false;
  FCPoint mp = firstPoint;
  if (firstTouch) {
    if (div.showHScrollBar || div.showVScrollBar) {
      if (div.downScrollHButton) {
        double contentWidth = getDivContentWidth(div);
        double subX = (mp.x - div.startPoint.x) / div.size.cx * contentWidth;
        double newScrollH = div.startScrollH + subX;
        if (newScrollH < 0) {
          newScrollH = 0;
        } else if (newScrollH > contentWidth - div.size.cx) {
          newScrollH = contentWidth - div.size.cx;
        }
        div.scrollH = newScrollH;
        div.getPaint().cancelClick = true;
        return;
      } else if (div.downScrollVButton) {
        double contentHeight = getDivContentHeight(div);
        double subY = (mp.y - div.startPoint.y) / div.size.cy * contentHeight;
        double newScrollV = div.startScrollV + subY;
        if (newScrollV < 0) {
          newScrollV = 0;
        } else if (newScrollV > contentHeight - div.size.cy) {
          newScrollV = contentHeight - div.size.cy;
        }
        div.scrollV = newScrollV;
        div.getPaint().cancelClick = true;
        return;
      }
    }
    if (div.allowDragScroll) {
      double contentWidth = getDivContentWidth(div);
      if (contentWidth > div.size.cx) {
        double subX = div.startPoint.x - mp.x;
        double newScrollH = div.startScrollH + subX;
        if (newScrollH < 0) {
          newScrollH = 0;
        } else if (newScrollH > contentWidth - div.size.cx) {
          newScrollH = contentWidth - div.size.cx;
        }
        div.scrollH = newScrollH;
        if (subX > 5 || subX < -5) {
          div.getPaint().cancelClick = true;
        }
      }
      double contentHeight = getDivContentHeight(div);
      if (contentHeight > div.size.cy) {
        double subY = div.startPoint.y - mp.y;
        double newScrollV = div.startScrollV + subY;
        if (newScrollV < 0) {
          newScrollV = 0;
        } else if (newScrollV > contentHeight - div.size.cy) {
          newScrollV = contentHeight - div.size.cy;
        }
        div.scrollV = newScrollV;
        if (subY > 5 || subY < -5) {
          div.getPaint().cancelClick = true;
        }
      }
    }
  } else {
    if (div.showHScrollBar) {
      double contentWidth = getDivContentWidth(div);
      if (contentWidth > 0 &&contentWidth > div.size.cx) {
        double sLeft = div.scrollH / contentWidth * div.size.cx;
        double sRight =
            (div.scrollH + div.size.cx) / contentWidth * div.size.cx;
        if (sRight - sLeft < div.scrollSize) {
          sRight = sLeft + div.scrollSize;
        }
        if (mp.x >= sLeft &&
            mp.x <= sRight &&
            mp.y >= div.size.cy - div.scrollSize &&
            mp.y <= div.size.cy) {
          div.hoverScrollHButton = true;
          return;
        } else {
          div.hoverScrollHButton = false;
        }
      }
    }
    if (div.showVScrollBar) {
      double contentHeight = getDivContentHeight(div);
      if (contentHeight > 0 && contentHeight > div.size.cy) {
        double sTop = div.scrollV / contentHeight * div.size.cy;
        double sBottom =
            (div.scrollV + div.size.cy) / contentHeight * div.size.cy;
        if (sBottom - sTop < div.scrollSize) {
          sBottom = sTop + div.scrollSize;
        }
        if (mp.x >= div.size.cx - div.scrollSize &&
            mp.x <= div.size.cx &&
            mp.y >= sTop &&
            mp.y <= sBottom) {
          div.hoverScrollVButton = true;
          return;
        } else {
          div.hoverScrollVButton = false;
        }
      }
    }
  }
}

/*
* 图层的鼠标按下方法
* div: 图层
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
* clicks:点击次数
*/
void touchDownDiv(FCDiv div, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint, int clicks) {
  FCPoint mp = firstPoint;
  div.startPoint = mp;
  div.touchDownTime = getTickCount();
  div.downScrollHButton = false;
  div.downScrollVButton = false;
  div.hoverScrollHButton = false;
  div.hoverScrollVButton = false;
  div.scrollAddSpeed = 0;
  div.isScrolling = false;
  if (div.showHScrollBar) {
    double contentWidth = getDivContentWidth(div);
    if (contentWidth > 0 && contentWidth > div.size.cx) {
      double sLeft = div.scrollH / contentWidth * div.size.cx;
      double sRight = (div.scrollH + div.size.cx) / contentWidth * div.size.cx;
      if (sRight - sLeft < div.scrollSize) {
        sRight = sLeft + div.scrollSize;
      }
      if (mp.x >= sLeft &&
          mp.x <= sRight &&
          mp.y >= div.size.cy - div.scrollSize &&
          mp.y <= div.size.cy) {
        div.downScrollHButton = true;
        div.startScrollH = div.scrollH;
        return;
      }
    }
  }
  if (div.showVScrollBar) {
    double contentHeight = getDivContentHeight(div);
    if (contentHeight > 0 && contentHeight > div.size.cy) {
      double sTop = div.scrollV / contentHeight * div.size.cy;
      double sBottom =
          (div.scrollV + div.size.cy) / contentHeight * div.size.cy;
      if (sBottom - sTop < div.scrollSize) {
        sBottom = sTop + div.scrollSize;
      }
      if (mp.x >= div.size.cx - div.scrollSize &&
          mp.x <= div.size.cx &&
          mp.y >= sTop &&
          mp.y <= sBottom) {
        div.downScrollVButton = true;
        div.startScrollV = div.scrollV;
        return;
      }
    }
  }
  if (div.allowDragScroll) {
    div.startScrollH = div.scrollH;
    div.startScrollV = div.scrollV;
  }
}

/*
* 图层的鼠标抬起方法
* div: 图层
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
* clicks:点击次数
*/
void touchUpDiv(FCDiv div, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint, int clicks) {
  div.hoverScrollHButton = false;
  div.hoverScrollVButton = false;
  if (firstTouch && !div.downScrollHButton && !div.downScrollVButton) {
    if (div.allowDragScroll) {
      int touchUpTime = getTickCount();
      int diff = (touchUpTime - div.touchDownTime) ~/ 1000;
      //加速滚动
      if (diff > 0 && diff < 500) {
        int sub1 =
            (30 * (((firstPoint.y - div.startPoint.y).abs()) / 20) / diff * 10)
                .toInt();
        int sub2 =
            (30 * (((firstPoint.x - div.startPoint.x).abs()) / 20) / diff * 10)
                .toInt();
        if (sub1.abs() > sub2.abs()) {
          if (firstPoint.y < div.startPoint.y) {
            div.scrollAddSpeed += sub1;
          } else {
            div.scrollAddSpeed -= sub1;
          }
          div.scrollDirection = 0;
        } else {
          if (firstPoint.x < div.startPoint.x) {
            div.scrollAddSpeed += sub2;
          } else {
            div.scrollAddSpeed -= sub2;
          }
          div.scrollDirection = 1;
        }
        div.isScrolling = true;
        if ((div.scrollAddSpeed).abs() > 0) {
          div.getPaint().cancelClick = true;
        }
      }
    }
  }
  div.downScrollHButton = false;
  div.downScrollVButton = false;
}

/*
* 图层的鼠标滚轮方法
* div:图层
* delta:滚轮值
*/
void touchWheelDiv(FCDiv div, int delta) {
  double oldScrollV = div.scrollV;
  if (delta > 0) {
    oldScrollV -= 10;
  } else if (delta < 0) {
    oldScrollV += 10;
  }
  double contentHeight = getDivContentHeight(div);
  if (contentHeight < div.size.cy) {
    div.scrollV = 0;
  } else {
    if (oldScrollV < 0) {
      oldScrollV = 0;
    } else if (oldScrollV > contentHeight - div.size.cy) {
      oldScrollV = contentHeight - div.size.cy;
    }
    div.scrollV = oldScrollV;
  }
}

/*
* 重绘图层
* div:视图
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawDiv(FCView div, FCPaint paint, FCRect clipRect) {
  if (div.backColor != "none") {
    paint.fillRoundRect(div.backColor, 0, 0, div.size.cx, div.size.cy, div.cornerRadius);
  }
}

/*
* 重绘图层边线
* div:视图
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawDivBorder(FCView div, FCPaint paint, FCRect clipRect) {
  if (div.borderColor != "none") {
    paint.drawRoundRect(div.borderColor, div.borderWidth, 0, 0, 0, div.size.cx, div.size.cy, div.cornerRadius);
  }
}

/*
* 检查图层滚动
* div:图层
*/
void checkDivScroll(FCDiv div) {
  if (div.isScrolling) {
    int sub = div.scrollAddSpeed ~/ 10;
    if (sub == 0 && div.scrollAddSpeed > 1) {
      sub = 1;
    } else if (sub == 0 && div.scrollAddSpeed < -1) {
      sub = -1;
    }
    div.scrollAddSpeed -= sub;
    if (sub.abs() <= 1) {
      div.scrollAddSpeed = 0;
      div.isScrolling = false;
      invalidateView(div);
    } else {
      double oldScrollV = div.scrollV + div.scrollAddSpeed;
      double oldScrollH = div.scrollH + div.scrollAddSpeed;
      if (div.scrollDirection == 0) {
        double contentHeight = getDivContentHeight(div);
        if (contentHeight < div.size.cy) {
          div.scrollV = 0;
        } else {
          if (oldScrollV < 0) {
            oldScrollV = 0;
          } else if (oldScrollV > contentHeight - div.size.cy) {
            oldScrollV = contentHeight - div.size.cy;
          }
          div.scrollV = oldScrollV;
        }
      } else {
        double contentWidth = getDivContentWidth(div);
        if (contentWidth < div.size.cx) {
          div.scrollH = 0;
        } else {
          if (oldScrollH < 0) {
            oldScrollH = 0;
          } else if (oldScrollH > contentWidth - div.size.cx) {
            oldScrollH = contentWidth - div.size.cx;
          }
          div.scrollH = oldScrollH;
        }
      }
      invalidateView(div);
    }
  }
}

/*
 * 快速添加表格列
 * grid:表格
 * columns:列名集合
 */
void fastAddGridColumns(FCGrid grid, List<String> columns) {
  int columnsSize = columns.length;
  for (int i = 0; i < columnsSize; i++) {
    FCGridColumn gridColumn = FCGridColumn();
    gridColumn.text = columns[i];
    grid.columns.add(gridColumn);
  }
}

/*
 * 快速添加表格行
 * grid:表格
 * datas:数据集合
 */
FCGridRow fastAddGridRow(FCGrid grid, List<String> datas) {
  FCGridRow gridRow = FCGridRow();
  int datasSize = datas.length;
  for (int i = 0; i < datasSize; i++) {
    FCGridCell gridCell = FCGridCell();
    gridCell.value = datas[i];
    gridRow.cells.add(gridCell);
  }
  return gridRow;
}

/*
 * 添加视图到单元格
 * view:视图
 * cell:单元格
 * grid:表格
 */
void addViewToGridCell(FCView view, FCGridCell cell, FCGrid grid){
    view.displayOffset = false;
    view.visible = false;
    cell.setView(view);
    addViewToParent(view, grid);
}

/*
* 绘制单元格
* grid:表格
* row:行
* column:列
* cell:单元格
* paint:绘图对象
* left:左侧坐标
* top:上方坐标
* right:右侧坐标
* bottom:下方坐标
*/
void drawGridCell(
    FCGrid grid,
    FCGridRow row,
    FCGridColumn column,
    FCGridCell cell,
    FCPaint paint,
    double left,
    double top,
    double right,
    double bottom) {
  if (cell.backColor != "none") {
    paint.fillRect(cell.backColor, left, top, right, bottom);
  }
  if (row.selected) {
    if (grid.selectedRowColor != "none") {
      paint.fillRect(grid.selectedRowColor, left, top, right, bottom);
    }
  }
  if (cell.borderColor != "none") {
    paint.drawRect(cell.borderColor, 1, 0, left, top, right, bottom);
  }
  if (cell.value.isNotEmpty) {
    String showText = cell.value;
    if (column.colType == "double") {
      if (cell.digit >= 0) {
        double numValue = double.parse(showText);
        showText = numValue.toStringAsFixed(cell.digit);
      }
    }
    FCSize tSize = paint.textSize(showText, cell.font);
    if (tSize.cx <= right - left) {
      if (column.cellAlign == "left") {
        paint.drawText(showText, cell.textColor, cell.font, left + 2,
            top + grid.rowHeight ~/ 2 - tSize.cy ~/ 2);
      } else if (column.cellAlign == "center") {
        paint.drawText(
            showText,
            cell.textColor,
            cell.font,
            left + (right - left - tSize.cx) ~/ 2,
            top + grid.rowHeight ~/ 2 - tSize.cy ~/ 2);
      } else if (column.cellAlign == "right") {
        paint.drawText(showText, cell.textColor, cell.font, right - tSize.cx,
            top + grid.rowHeight ~/ 2 - tSize.cy ~/ 2);
      }
    } else {
      paint.drawTextAutoEllipsis(
          showText,
          cell.textColor,
          cell.font,
          left,
          top + grid.rowHeight ~/ 2 - tSize.cy ~/ 2,
          right,
          top + grid.rowHeight ~/ 2 + tSize.cy ~/ 2);
    }
  }
}

/*
* 绘制列
* grid:表格
* column:列
* paint:绘图对象
* left:左侧坐标
* top:上方坐标
* right:右侧坐标
* bottom:下方坐标
*/
void drawGridColumn(FCGrid grid, FCGridColumn column, FCPaint paint,
    double left, double top, double right, double bottom) {
  FCSize tSize = paint.textSize(column.text, column.font);
  if (column.backColor != "none") {
    paint.fillRect(column.backColor, left, top, right, bottom);
  }
  if (column.borderColor != "none") {
    paint.drawRect(column.borderColor, 1, 0, left, top, right, bottom);
  }
  paint.drawText(
      column.text,
      column.textColor,
      column.font,
      left + (column.width - tSize.cx) / 2,
      top + (grid.headerHeight - tSize.cy) / 2);
  if (column.sort == "asc") {
    paint.beginPath();
    double cR = (bottom - top) / 4;
    double oX = right - cR * 2, oY = top + (bottom - top) / 2;
    paint.addLine(oX, oY - cR, oX - cR, oY + cR);
    paint.addLine(oX - cR, oY + cR, oX + cR, oY + cR);
    paint.addLine(oX + cR, oY + cR, oX, oY - cR);
    paint.fillPath(column.textColor);
    paint.closePath();
  } else if (column.sort == "desc") {
    paint.beginPath();
    double cR = (bottom - top) / 4;
    double oX = right - cR * 2, oY = top + (bottom - top) / 2;
    paint.addLine(oX, oY + cR, oX - cR, oY - cR);
    paint.addLine(oX - cR, oY - cR, oX + cR, oY - cR);
    paint.addLine(oX + cR, oY - cR, oX, oY + cR);
    paint.fillPath(column.textColor);
    paint.closePath();
  }
}

/*
* 绘制表格
* grid:表格
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawGrid(FCGrid grid, FCPaint paint, FCRect clipRect) {
  double cTop = -grid.scrollV + grid.headerHeight;
  //绘制行
  double colLeft = 0;
  for (int i = 0; i < grid.views.length; i++) {
    grid.views[i].visible = false;
  }
  for (int i = 0; i < grid.columns.length; i++) {
    if (grid.columns[i].widthStr.isNotEmpty) {
      String newWidthStr = grid.columns[i].widthStr.replaceAll("%", "");
      grid.columns[i].width = double.parse(newWidthStr) * grid.size.cx / 100;
    }
    FCRect colRect =
        FCRect(colLeft, 0, colLeft + grid.columns[i].width, grid.headerHeight);
    grid.columns[i].bounds = colRect;
    grid.columns[i].index = i;
    colLeft += grid.columns[i].width;
  }
  for (int i = 0; i < grid.rows.length; i++) {
    FCGridRow row = grid.rows[i];
    row.index = i;
    if (row.visible) {
      double rTop = cTop, rBottom = cTop + grid.rowHeight;
      if (rBottom >= 0 && cTop <= grid.size.cy) {
        for (int j = 0; j < row.cells.length; j++) {
          FCGridCell cell = row.cells[j];
          FCGridColumn gridColumn = cell.getColumn();
          if (!gridColumn.isNotEmpty) {
            gridColumn = grid.columns[j];
          }
          if (gridColumn.visible) {
            if (!gridColumn.frozen) {
              double cellWidth = gridColumn.width;
              int colSpan = cell.colSpan;
              if (colSpan > 1) {
                for (int n = 1; n < colSpan; n++) {
                  FCGridColumn spanColumn = grid.columns[gridColumn.index + n];
                  if (spanColumn.isNotEmpty && spanColumn.visible) {
                    cellWidth += spanColumn.width;
                  }
                }
              }
              double cellHeight = grid.rowHeight;
              int rowSpan = cell.rowSpan;
              if (rowSpan > 1) {
                for (int n = 1; n < rowSpan; n++) {
                  FCGridRow spanRow = grid.rows[i + n];
                  if (spanRow.isNotEmpty && spanRow.visible) {
                    cellHeight += grid.rowHeight;
                  }
                }
              }
              FCRect cRect = FCRect(
                  gridColumn.bounds.left - grid.scrollH,
                  rTop,
                  gridColumn.bounds.left + cellWidth - grid.scrollH,
                  rTop + cellHeight);
              if (cRect.right >= 0 && cRect.left < grid.size.cx) {
                if (paint.hasPaintGridCellEvent) {
                  paint.onPaintGridCell(grid, row, gridColumn, cell, paint,
                      cRect.left, cRect.top, cRect.right, cRect.bottom);
                } else {
                  drawGridCell(grid, row, gridColumn, cell, paint, cRect.left,
                      cRect.top, cRect.right, cRect.bottom);
                }
                if(cell.view.isNotEmpty){
                    FCView cellView = cell.getView();
                    cellView.visible = false;
                    cellView.location = FCPoint(cRect.left + grid.scrollH, cRect.top + grid.scrollV);
                    cellView.size = FCSize(cRect.right - cRect.left, cRect.bottom - cRect.top);
                }
              }
            }
          }
        }
      }
      if (rBottom >= 0 && cTop <= grid.size.cy) {
        for (int j = 0; j < row.cells.length; j++) {
          FCGridCell cell = row.cells[j];
          FCGridColumn gridColumn = cell.getColumn();
          if (!gridColumn.isNotEmpty) {
            gridColumn = grid.columns[j];
          }
          if (gridColumn.visible) {
            if (gridColumn.frozen) {
              double cellWidth = gridColumn.width;
              int colSpan = cell.colSpan;
              if (colSpan > 1) {
                for (int n = 1; n < colSpan; n++) {
                  FCGridColumn spanColumn = grid.columns[gridColumn.index + n];
                  if (spanColumn.isNotEmpty && spanColumn.visible) {
                    cellWidth += spanColumn.width;
                  }
                }
              }
              double cellHeight = grid.rowHeight;
              int rowSpan = cell.rowSpan;
              if (rowSpan > 1) {
                for (int n = 1; n < rowSpan; n++) {
                  FCGridRow spanRow = grid.rows[i + n];
                  if (spanRow.isNotEmpty && spanRow.visible) {
                    cellHeight += grid.rowHeight;
                  }
                }
              }
              FCRect cRect = FCRect(gridColumn.bounds.left, rTop,
                  gridColumn.bounds.left + cellWidth, rTop + cellHeight);
              if (cRect.right >= 0 && cRect.left < grid.size.cx) {
                if (paint.hasPaintGridCellEvent) {
                  paint.onPaintGridCell(grid, row, gridColumn, cell, paint,
                      cRect.left, cRect.top, cRect.right, cRect.bottom);
                } else {
                  drawGridCell(grid, row, gridColumn, cell, paint, cRect.left,
                      cRect.top, cRect.right, cRect.bottom);
                }
                if(cell.view.isNotEmpty){
                    FCView cellView = cell.getView();
                    cellView.visible = false;
                    cellView.location = FCPoint(cRect.left + grid.scrollH, cRect.top + grid.scrollV);
                    cellView.size = FCSize(cRect.right - cRect.left, cRect.bottom - cRect.top);
                }
              }
            }
          }
        }
      }
      if (cTop > grid.size.cy) {
        break;
      }
      cTop += grid.rowHeight;
    }
  }
}

/*
* 绘制表格的滚动条
* grid:表格
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawGridScrollBar(FCGrid grid, FCPaint paint, FCRect clipRect) {
  grid.hScrollIsVisible = false;
  grid.vScrollIsVisible = false;
if (grid.headerHeight > 0) {
   double cLeft = -grid.scrollH;
    //绘制列
    for (int i = 0; i < grid.columns.length; i++) {
      FCGridColumn gridColumn = grid.columns[i];
      if (grid.columns[i].visible) {
        if (!gridColumn.frozen) {
          if (paint.hasPaintGridColumnEvent) {
            paint.onPaintGridColumn(grid, grid.columns[i], paint, cLeft, 0,
                cLeft + gridColumn.width, grid.headerHeight);
          } else {
            drawGridColumn(grid, grid.columns[i], paint, cLeft, 0,
                cLeft + gridColumn.width, grid.headerHeight);
          }
        }
        cLeft += gridColumn.width;
      }
    }
    cLeft = 0;
    for (int i = 0; i < grid.columns.length; i++) {
      FCGridColumn gridColumn = grid.columns[i];
      if (grid.columns[i].visible) {
        if (gridColumn.frozen) {
          if (paint.hasPaintGridColumnEvent) {
            paint.onPaintGridColumn(grid, grid.columns[i], paint, cLeft, 0,
                cLeft + gridColumn.width, grid.headerHeight);
          } else {
            drawGridColumn(grid, grid.columns[i], paint, cLeft, 0,
                cLeft + gridColumn.width, grid.headerHeight);
          }
        }
        cLeft += gridColumn.width;
      }
    }
  }
  if (grid.showHScrollBar) {
    double contentWidth = getGridContentWidth(grid);
    if (contentWidth > 0 && contentWidth > grid.size.cx) {
      double sLeft = grid.scrollH / contentWidth * grid.size.cx;
      double sRight =
          (grid.scrollH + grid.size.cx) / contentWidth * grid.size.cx;
      if (sRight - sLeft < grid.scrollSize) {
        sRight = sLeft + grid.scrollSize;
      }
      if (grid.getPaint().getTouchMoveView() == grid &&
          (grid.hoverScrollHButton || grid.downScrollHButton)) {
        paint.fillRect(grid.scrollBarHoveredColor, sLeft,
            grid.size.cy - grid.scrollSize, sRight, grid.size.cy);
      } else {
        paint.fillRect(grid.scrollBarColor, sLeft,
            grid.size.cy - grid.scrollSize, sRight, grid.size.cy);
      }
      grid.hScrollIsVisible = true;
    }
  }
  if (grid.showVScrollBar) {
    double contentHeight = getGridContentHeight(grid);
    if (contentHeight > 0 && contentHeight > grid.size.cy - grid.headerHeight) {
      double sTop = grid.headerHeight +
          grid.scrollV /
              contentHeight *
              (grid.size.cy - grid.headerHeight - grid.scrollSize);
      double sBottom = sTop +
          ((grid.size.cy - grid.headerHeight - grid.scrollSize)) /
              contentHeight *
              (grid.size.cy - grid.headerHeight - grid.scrollSize);
      if (sBottom - sTop < grid.scrollSize) {
        sBottom = sTop + grid.scrollSize;
      }
      if (grid.getPaint().getTouchMoveView() == grid &&
          (grid.hoverScrollVButton || grid.downScrollVButton)) {
        paint.fillRect(grid.scrollBarHoveredColor,
            grid.size.cx - grid.scrollSize, sTop, grid.size.cx, sBottom);
      } else {
        paint.fillRect(grid.scrollBarColor, grid.size.cx - grid.scrollSize,
            sTop, grid.size.cx, sBottom);
      }
      grid.vScrollIsVisible = true;
    }
  }
}

/*
* 获取内容的宽度
* grid:表格
*/
double getGridContentWidth(FCGrid grid) {
  double cWidth = 0;
  for (int i = 0; i < grid.columns.length; i++) {
    if (grid.columns[i].visible) {
      cWidth += grid.columns[i].width;
    }
  }
  return cWidth;
}

/*
* 获取内容的高度
* grid:表格
*/
double getGridContentHeight(FCGrid grid) {
  double cHeight = 0;
  for (int i = 0; i < grid.rows.length; i++) {
    if (grid.rows[i].visible) {
      cHeight += grid.rowHeight;
    }
  }
  return cHeight;
}

/*
* 表格的鼠标移动方法
* grid: 表格
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
*/
void touchMoveGrid(FCGrid grid, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint) {
  grid.hoverScrollHButton = false;
  grid.hoverScrollVButton = false;
  FCPoint mp = firstPoint;
  FCPaint paint = grid.getPaint();
  if (firstTouch) {
    if (paint.resizeColumnState != 0) {
      FCGridColumn gridColumn = grid.columns[paint.resizeColumnIndex];
      double newWidth =
          paint.resizeColumnBeginWidth + (mp.x - grid.startPoint.x);
      if (newWidth > 10) {
        gridColumn.width = newWidth;
      }
      return;
    }
    if (grid.showHScrollBar || grid.showVScrollBar) {
      if (grid.downScrollHButton) {
        double contentWidth = getGridContentWidth(grid);
        double subX = (mp.x - grid.startPoint.x) / grid.size.cx * contentWidth;
        double newScrollH = grid.startScrollH + subX;
        if (newScrollH < 0) {
          newScrollH = 0;
        } else if (newScrollH > contentWidth - grid.size.cx) {
          newScrollH = contentWidth - grid.size.cx;
        }
        grid.scrollH = newScrollH;
        paint.cancelClick = true;
        return;
      } else if (grid.downScrollVButton) {
        double contentHeight = getGridContentHeight(grid);
        double subY = (mp.y - grid.startPoint.y) /
            (grid.size.cy - grid.headerHeight - grid.scrollSize) *
            contentHeight;
        double newScrollV = grid.startScrollV + subY;
        if (newScrollV < 0) {
          newScrollV = 0;
        } else if (newScrollV >
            contentHeight -
                (grid.size.cy - grid.headerHeight - grid.scrollSize)) {
          newScrollV = contentHeight -
              (grid.size.cy - grid.headerHeight - grid.scrollSize);
        }
        grid.scrollV = newScrollV;
        paint.cancelClick = true;
        return;
      }
    }
    if (grid.allowDragScroll) {
      double contentWidth = getGridContentWidth(grid);
      if (contentWidth > grid.size.cx - grid.scrollSize) {
        double subX = grid.startPoint.x - mp.x;
        double newScrollH = grid.startScrollH + subX;
        if (newScrollH < 0) {
          newScrollH = 0;
        } else if (newScrollH > contentWidth - grid.size.cx) {
          newScrollH = contentWidth - grid.size.cx;
        }
        grid.scrollH = newScrollH;
        if (subX > 5 || subX < -5) {
          paint.cancelClick = true;
        }
      }
      double contentHeight = getGridContentHeight(grid);
      if (contentHeight > grid.size.cy - grid.headerHeight - grid.scrollSize) {
        double subY = grid.startPoint.y - mp.y;
        double newScrollV = grid.startScrollV + subY;
        if (newScrollV < 0) {
          newScrollV = 0;
        } else if (newScrollV >
            contentHeight -
                (grid.size.cy - grid.headerHeight - grid.scrollSize)) {
          newScrollV = contentHeight -
              (grid.size.cy - grid.headerHeight - grid.scrollSize);
        }
        grid.scrollV = newScrollV;
        if (subY > 5 || subY < -5) {
          paint.cancelClick = true;
        }
      }
    }
  } else {
    if (grid.showHScrollBar) {
      double contentWidth = getGridContentWidth(grid);
      if (contentWidth > 0 && contentWidth > grid.size.cx - grid.scrollSize) {
        double sLeft = grid.scrollH / contentWidth * grid.size.cx;
        double sRight =
            (grid.scrollH + grid.size.cx) / contentWidth * grid.size.cx;
        if (sRight - sLeft < grid.scrollSize) {
          sRight = sLeft + grid.scrollSize;
        }
        if (mp.x >= sLeft &&
            mp.x <= sRight &&
            mp.y >= grid.size.cy - grid.scrollSize &&
            mp.y <= grid.size.cy) {
          grid.hoverScrollHButton = true;
          return;
        } else {
          grid.hoverScrollHButton = false;
        }
      }
    }
    if (grid.showVScrollBar) {
      double contentHeight = getGridContentHeight(grid);
      if (contentHeight > 0 && contentHeight > grid.size.cy - grid.headerHeight - grid.scrollSize) {
        double sTop = grid.headerHeight +
            grid.scrollV /
                contentHeight *
                (grid.size.cy - grid.headerHeight - grid.scrollSize);
        double sBottom = grid.headerHeight +
            (grid.scrollV +
                    (grid.size.cy - grid.headerHeight - grid.scrollSize)) /
                contentHeight *
                (grid.size.cy - grid.headerHeight - grid.scrollSize);
        if (sBottom - sTop < grid.scrollSize) {
          sBottom = sTop + grid.scrollSize;
        }
        if (mp.x >= grid.size.cx - grid.scrollSize &&
            mp.x <= grid.size.cx &&
            mp.y >= sTop &&
            mp.y <= sBottom) {
          grid.hoverScrollVButton = true;
          return;
        } else {
          grid.hoverScrollVButton = false;
        }
      }
    }
  }
}

/*
* 表格的鼠标按下方法
* grid: 表格
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
* clicks:点击次数
*/
void touchDownGrid(FCGrid grid, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint, int clicks) {
  FCPoint mp = firstPoint;
  grid.startPoint = mp;
  grid.touchDownTime = getTickCount();
  grid.downScrollHButton = false;
  grid.downScrollVButton = false;
  grid.hoverScrollHButton = false;
  grid.hoverScrollVButton = false;
  grid.scrollAddSpeed = 0;
  grid.isScrolling = false;
  if (grid.showHScrollBar) {
    double contentWidth = getGridContentWidth(grid);
    if (contentWidth > 0 && contentWidth > grid.size.cx - grid.scrollSize) {
      double sLeft = grid.scrollH / contentWidth * grid.size.cx;
      double sRight =
          (grid.scrollH + grid.size.cx) / contentWidth * grid.size.cx;
      if (sRight - sLeft < grid.scrollSize) {
        sRight = sLeft + grid.scrollSize;
      }
      if (mp.x >= sLeft &&
          mp.x <= sRight &&
          mp.y >= grid.size.cy - grid.scrollSize &&
          mp.y <= grid.size.cy) {
        grid.downScrollHButton = true;
        grid.startScrollH = grid.scrollH;
        return;
      }
    }
  }
  if (grid.showVScrollBar) {
    double contentHeight = getGridContentHeight(grid);
    if (contentHeight > 0 && contentHeight > grid.size.cy - grid.headerHeight - grid.scrollSize) {
      double sTop = grid.headerHeight +
          grid.scrollV /
              contentHeight *
              (grid.size.cy - grid.headerHeight - grid.scrollSize);
      double sBottom = grid.headerHeight +
          (grid.scrollV +
                  (grid.size.cy - grid.headerHeight - grid.scrollSize)) /
              contentHeight *
              (grid.size.cy - grid.headerHeight - grid.scrollSize);
      if (sBottom - sTop < grid.scrollSize) {
        sBottom = sTop + grid.scrollSize;
      }
      if (mp.x >= grid.size.cx - grid.scrollSize &&
          mp.x <= grid.size.cx &&
          mp.y >= sTop &&
          mp.y <= sBottom) {
        grid.downScrollVButton = true;
        grid.startScrollV = grid.scrollV;
        return;
      }
    }
  }
  if (grid.allowDragScroll) {
    grid.startScrollH = grid.scrollH;
    grid.startScrollV = grid.scrollV;
  }
  double colLeft = 0;
  for (int i = 0; i < grid.columns.length; i++) {
    FCRect colRect =
        FCRect(colLeft, 0, colLeft + grid.columns[i].width, grid.headerHeight);
    grid.columns[i].bounds = colRect;
    grid.columns[i].index = i;
    colLeft += grid.columns[i].width;
  }
  FCPaint paint = grid.getPaint();
  paint.resizeColumnState = 0;
  paint.resizeColumnBeginWidth = 0;
  if (grid.headerHeight > 0 && mp.y <= grid.headerHeight) {
    //绘制列
    for (int i = 0; i < grid.columns.length; i++) {
      FCGridColumn gridColumn = grid.columns[i];
      if (gridColumn.visible) {
        FCRect bounds = gridColumn.bounds;
        if (mp.x >= bounds.left - grid.scrollH && mp.x <= bounds.right - grid.scrollH) {
          if (gridColumn.index > 0 && mp.x < bounds.left + 5 - grid.scrollH) {
            paint.resizeColumnState = 1;
            paint.resizeColumnBeginWidth =
            grid.columns[gridColumn.index - 1].bounds.right -
            grid.columns[gridColumn.index - 1].bounds.left;
            paint.resizeColumnIndex = gridColumn.index - 1;
            return;
          } else if (mp.x > bounds.right - 5 - grid.scrollH) {
            paint.resizeColumnState = 2;
            paint.resizeColumnBeginWidth = bounds.right - bounds.left;
            paint.resizeColumnIndex = gridColumn.index;
            return;
          }
          break;
        }
      }
    }
  }

  double cTop = -grid.scrollV + grid.headerHeight;
  //绘制行
  colLeft = 0;
  for (int i = 0; i < grid.columns.length; i++) {
    FCRect colRect =
        FCRect(colLeft, 0, colLeft + grid.columns[i].width, grid.headerHeight);
    grid.columns[i].bounds = colRect;
    grid.columns[i].index = i;
    colLeft += grid.columns[i].width;
  }
  if (grid.headerHeight > 0 && firstPoint.y <= grid.headerHeight) {
   double cLeft = 0;
    //绘制列
    for (int i = 0; i < grid.columns.length; i++) {
      FCGridColumn gridColumn = grid.columns[i];
      if (grid.columns[i].visible) {
        if (gridColumn.frozen) {
          if (firstPoint.x >= cLeft &&
              firstPoint.x <= cLeft + gridColumn.width) {
            for (int j = 0; j < grid.columns.length; j++) {
              FCGridColumn tColumn = grid.columns[j];
              if (tColumn == gridColumn) {
                if (tColumn.allowSort) {
                  if (tColumn.sort == "none" || tColumn.sort == "desc") {
                    tColumn.sort = "asc";
                    if (tColumn.colType == "double") {
                      grid.rows.sort((a, b) => double.parse(b.cells[j].value)
                          .compareTo(double.parse(a.cells[j].value)));
                    } else if (tColumn.colType == "int") {
                      grid.rows.sort((a, b) => int.parse(b.cells[j].value)
                          .compareTo(int.parse(a.cells[j].value)));
                    } else if (tColumn.colType == "bool") {
                      grid.rows.sort((a, b) =>
                          (b.cells[j].value == "true" ? 1 : 0)
                              .compareTo(a.cells[j].value == "true" ? 1 : 0));
                    } else {
                      grid.rows.sort((a, b) =>
                          b.cells[j].value.compareTo(a.cells[j].value));
                    }
                  } else {
                    tColumn.sort = "desc";
                    if (tColumn.colType == "double") {
                      grid.rows.sort((a, b) => double.parse(a.cells[j].value)
                          .compareTo(double.parse(b.cells[j].value)));
                    } else if (tColumn.colType == "int") {
                      grid.rows.sort((a, b) => int.parse(a.cells[j].value)
                          .compareTo(int.parse(b.cells[j].value)));
                    } else if (tColumn.colType == "bool") {
                      grid.rows.sort((a, b) =>
                          (a.cells[j].value == "true" ? 1 : 0)
                              .compareTo(b.cells[j].value == "true" ? 1 : 0));
                    } else {
                      grid.rows.sort((a, b) =>
                          a.cells[j].value.compareTo(b.cells[j].value));
                    }
                  }
                } else {
                  tColumn.sort = "none";
                }
              } else {
                tColumn.sort = "none";
              }
            }
            if (grid.getPaint().hasClickGridColumnEvent) {
              grid.getPaint().onClickGridColumn(grid, gridColumn, firstTouch,
                  firstPoint, secondTouch, secondPoint, clicks);
            }
            return;
          }
        }
        cLeft += gridColumn.width;
      }
    }
     cLeft = -grid.scrollH;
    for (int i = 0; i < grid.columns.length; i++) {
      FCGridColumn gridColumn = grid.columns[i];
      if (grid.columns[i].visible) {
        if (!gridColumn.frozen) {
          if (firstPoint.x >= cLeft &&
              firstPoint.x <= cLeft + gridColumn.width) {
            for (int j = 0; j < grid.columns.length; j++) {
              FCGridColumn tColumn = grid.columns[j];
              if (tColumn == gridColumn) {
                if (tColumn.allowSort) {
                  if (tColumn.sort == "none" || tColumn.sort == "desc") {
                    tColumn.sort = "asc";
                    if (tColumn.colType == "double") {
                      grid.rows.sort((a, b) => double.parse(b.cells[j].value)
                          .compareTo(double.parse(a.cells[j].value)));
                    } else if (tColumn.colType == "int") {
                      grid.rows.sort((a, b) => int.parse(b.cells[j].value)
                          .compareTo(int.parse(a.cells[j].value)));
                    } else if (tColumn.colType == "bool") {
                      grid.rows.sort((a, b) =>
                          (b.cells[j].value == "true" ? 1 : 0)
                              .compareTo(a.cells[j].value == "true" ? 1 : 0));
                    } else {
                      grid.rows.sort((a, b) =>
                          b.cells[j].value.compareTo(a.cells[j].value));
                    }
                  } else {
                    tColumn.sort = "desc";
                    if (tColumn.colType == "double") {
                      grid.rows.sort((a, b) => double.parse(a.cells[j].value)
                          .compareTo(double.parse(b.cells[j].value)));
                    } else if (tColumn.colType == "int") {
                      grid.rows.sort((a, b) => int.parse(a.cells[j].value)
                          .compareTo(int.parse(b.cells[j].value)));
                    } else if (tColumn.colType == "bool") {
                      grid.rows.sort((a, b) =>
                          (a.cells[j].value == "true" ? 1 : 0)
                              .compareTo(b.cells[j].value == "true" ? 1 : 0));
                    } else {
                      grid.rows.sort((a, b) =>
                          a.cells[j].value.compareTo(b.cells[j].value));
                    }
                  }
                } else {
                  tColumn.sort = "none";
                }
              } else {
                tColumn.sort = "none";
              }
            }
            if (grid.getPaint().hasClickGridColumnEvent) {
              grid.getPaint().onClickGridColumn(grid, gridColumn, firstTouch,
                  firstPoint, secondTouch, secondPoint, clicks);
            }
            return;
          }
        }
        cLeft += gridColumn.width;
      }
    }
  }
  for (int i = 0; i < grid.rows.length; i++) {
    FCGridRow row = grid.rows[i];
    if (row.visible) {
      double rTop = cTop, rBottom = cTop + grid.rowHeight;
      if (rBottom >= 0 && cTop <= grid.size.cy) {
        for (int j = 0; j < row.cells.length; j++) {
          FCGridCell cell = row.cells[j];
          FCGridColumn gridColumn = cell.getColumn();
          if (!gridColumn.isNotEmpty) {
            gridColumn = grid.columns[j];
          }
          if (gridColumn.visible) {
            if (gridColumn.frozen) {
              double cellWidth = gridColumn.width;
              int colSpan = cell.colSpan;
              if (colSpan > 1) {
                for (int n = 1; n < colSpan; n++) {
                  FCGridColumn spanColumn = grid.columns[gridColumn.index + n];
                  if (spanColumn.isNotEmpty && spanColumn.visible) {
                    cellWidth += spanColumn.width;
                  }
                }
              }
              double cellHeight = grid.rowHeight;
              int rowSpan = cell.rowSpan;
              if (rowSpan > 1) {
                for (int n = 1; n < rowSpan; n++) {
                  FCGridRow spanRow = grid.rows[i + n];
                  if (spanRow.isNotEmpty && spanRow.visible) {
                    cellHeight += grid.rowHeight;
                  }
                }
              }

              FCRect cRect = FCRect(
                  gridColumn.bounds.left,
                  rTop,
                  gridColumn.bounds.left + cellWidth,
                  rTop + cellHeight);
              if (cRect.right >= 0 && cRect.left < grid.size.cx) {
                if (firstPoint.x >= cRect.left &&
                    firstPoint.x <= cRect.right &&
                    firstPoint.y >= cRect.top &&
                    firstPoint.y <= cRect.bottom) {
                  for (int r = 0; r < grid.rows.length; r++) {
                    FCGridRow subRow = grid.rows[r];
                    if (subRow == row) {
                      subRow.selected = true;
                    } else {
                      subRow.selected = false;
                    }
                  }
                  if (grid.getPaint().hasClickGridCellEvent) {
                    grid.getPaint().onClickGridCell(grid, row, gridColumn, cell,
                        firstTouch, firstPoint, secondTouch, secondPoint, clicks);
                  }
                  return;
                }
              }
            }
          }
        }
      }
      if (rBottom >= 0 && cTop <= grid.size.cy) {
        for (int j = 0; j < row.cells.length; j++) {
          FCGridCell cell = row.cells[j];
          FCGridColumn gridColumn = cell.getColumn();
          if (!gridColumn.isNotEmpty) {
            gridColumn = grid.columns[j];
          }
          if (gridColumn.visible) {
            if (!gridColumn.frozen) {
              double cellWidth = gridColumn.width;
              int colSpan = cell.colSpan;
              if (colSpan > 1) {
                for (int n = 1; n < colSpan; n++) {
                  FCGridColumn spanColumn = grid.columns[gridColumn.index + n];
                  if (spanColumn.isNotEmpty && spanColumn.visible) {
                    cellWidth += spanColumn.width;
                  }
                }
              }
              double cellHeight = grid.rowHeight;
              int rowSpan = cell.rowSpan;
              if (rowSpan > 1) {
                for (int n = 1; n < rowSpan; n++) {
                  FCGridRow spanRow = grid.rows[i + n];
                  if (spanRow.isNotEmpty && spanRow.visible) {
                    cellHeight += grid.rowHeight;
                  }
                }
              }
              FCRect cRect = FCRect(gridColumn.bounds.left - grid.scrollH, rTop,
                  gridColumn.bounds.left + cellWidth - grid.scrollH, rTop + cellHeight);
              if (cRect.right >= 0 && cRect.left < grid.size.cx) {
                if (firstPoint.x >= cRect.left &&
                    firstPoint.x <= cRect.right &&
                    firstPoint.y >= cRect.top &&
                    firstPoint.y <= cRect.bottom) {
                  for (int r = 0; r < grid.rows.length; r++) {
                    FCGridRow subRow = grid.rows[r];
                    if (subRow == row) {
                      subRow.selected = true;
                    } else {
                      subRow.selected = false;
                    }
                  }
                  if (grid.getPaint().hasClickGridCellEvent) {
                    grid.getPaint().onClickGridCell(grid, row, gridColumn, cell,
                        firstTouch, firstPoint, secondTouch, secondPoint, clicks);
                  }
                  return;
                }
              }
            }
          }
        }
      }
      if (cTop > grid.size.cy) {
        break;
      }
      cTop += grid.rowHeight;
    }
  }
}

/*
* 表格的鼠标抬起方法
* grid: 表格
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
* clicks:点击次数
*/
void touchUpGrid(FCGrid grid, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint, int clicks) {
  grid.hoverScrollHButton = false;
  grid.hoverScrollVButton = false;
  if (firstTouch) {
    if (grid.allowDragScroll) {
      int touchUpTime = getTickCount();
      int diff = (touchUpTime - grid.touchDownTime) ~/ 1000;
      //加速滚动
      if (diff > 0 && diff < 500) {
        int sub1 =
            (30 * (((firstPoint.y - grid.startPoint.y).abs()) / 20) / diff * 10)
                .toInt();
        int sub2 =
            (30 * (((firstPoint.x - grid.startPoint.x).abs()) / 20) / diff * 10)
                .toInt();
        if (sub1.abs() > sub2.abs()) {
          if (firstPoint.y < grid.startPoint.y) {
            grid.scrollAddSpeed += sub1;
          } else {
            grid.scrollAddSpeed -= sub1;
          }
          grid.scrollDirection = 0;
        } else {
          if (firstPoint.x < grid.startPoint.x) {
            grid.scrollAddSpeed += sub2;
          } else {
            grid.scrollAddSpeed -= sub2;
          }
          grid.scrollDirection = 1;
        }
        grid.isScrolling = true;
        if ((grid.scrollAddSpeed).abs() > 0) {
          grid.getPaint().cancelClick = true;
        }
      }
    }
  }
  grid.downScrollHButton = false;
  grid.downScrollVButton = false;
}

/*
* 表格的鼠标滚轮方法
* grid:表格
* delta:滚轮值
*/
void touchWheelGrid(FCGrid grid, int delta) {
  double oldScrollV = grid.scrollV;
  if (delta > 0) {
    oldScrollV -= grid.rowHeight;
  } else if (delta < 0) {
    oldScrollV += grid.rowHeight;
  }
  double contentHeight = getGridContentHeight(grid);
  if (contentHeight < grid.size.cy - grid.headerHeight - grid.scrollSize) {
    grid.scrollV = 0;
  } else {
    if (oldScrollV < 0) {
      oldScrollV = 0;
    } else if (oldScrollV >
        contentHeight - grid.size.cy + grid.headerHeight + grid.scrollSize) {
      oldScrollV =
          contentHeight - grid.size.cy + grid.headerHeight + grid.scrollSize;
    }
    grid.scrollV = oldScrollV;
  }
}

/*
* 检查表格滚动
* grid:表格
*/
void checkGridScroll(FCGrid grid) {
  if (grid.isScrolling) {
    int sub = grid.scrollAddSpeed ~/ 10;
    if (sub == 0 && grid.scrollAddSpeed > 1) {
      sub = 1;
    } else if (sub == 0 && grid.scrollAddSpeed < -1) {
      sub = -1;
    }
    grid.scrollAddSpeed -= sub;
    if (sub.abs() <= 1) {
      grid.isScrolling = false;
      grid.scrollAddSpeed = 0;
      invalidateView(grid);
    } else {
      double oldScrollV = grid.scrollV + grid.scrollAddSpeed;
      double oldScrollH = grid.scrollH + grid.scrollAddSpeed;
      if (grid.scrollDirection == 0) {
        double contentHeight = getGridContentHeight(grid);
        if (contentHeight < grid.size.cy - grid.scrollSize) {
          grid.scrollV = 0;
        } else {
          if (oldScrollV < 0) {
            oldScrollV = 0;
          } else if (oldScrollV >
              contentHeight -
                  grid.size.cy +
                  grid.headerHeight +
                  grid.scrollSize) {
            oldScrollV = contentHeight -
                grid.size.cy +
                grid.headerHeight +
                grid.scrollSize;
          }
          grid.scrollV = oldScrollV;
        }
      } else {
        double contentWidth = getGridContentWidth(grid);
        if (contentWidth < grid.size.cx - grid.headerHeight - grid.scrollSize) {
          grid.scrollH = 0;
        } else {
          if (oldScrollH < 0) {
            oldScrollH = 0;
          } else if (oldScrollH > contentWidth - grid.size.cx) {
            oldScrollH = contentWidth - grid.size.cx;
          }
          grid.scrollH = oldScrollH;
        }
      }
      invalidateView(grid);
    }
  }
}

/*
* 重置布局图层
* layout:布局层
*/
bool resetLayoutDiv(FCLayoutDiv layout) {
  bool reset = false;
  FCPadding padding = FCPadding(0, 0, 0, 0);
  if (layout.padding.isNotEmpty) {
    padding = layout.padding;
  }
  int vPos = 0;
  double left = padding.left,
      top = padding.top,
      width = layout.size.cx - padding.left - padding.right,
      height = layout.size.cy - padding.top - padding.bottom;
  for (int i = 0; i < layout.views.length; i++) {
    FCView view = layout.views[i];
    if (view.visible) {
      FCSize size = view.size;
      FCPadding margin = FCPadding(0, 0, 0, 0);
      if (view.margin.isNotEmpty) {
        margin = view.margin;
      }
      double cLeft = view.location.x,
          cTop = view.location.y,
          cWidth = size.cx,
          cHeight = size.cy;
      double nLeft = cLeft, nTop = cTop, nWidth = cWidth, nHeight = cHeight;
      if (layout.layoutStyle == "bottomtotop") {
        if (i == 0) {
          top = height - padding.top;
        }
        double lWidth = 0;
        if (layout.autoWrap) {
          lWidth = size.cx;
          double lTop = top - margin.top - cHeight - margin.bottom;
          if (lTop < padding.top) {
            if (vPos != 0) {
              left += cWidth + margin.left;
            }
            top = height - padding.top;
          }
        } else {
          lWidth = width - margin.left - margin.right;
        }
        top -= cHeight + margin.bottom;
        nLeft = left + margin.left;
        nWidth = lWidth;
        nTop = top;
      } else if (layout.layoutStyle == "lefttoright") {
        double lHeight = 0;
        if (layout.autoWrap) {
          lHeight = size.cy;
          double lRight = left + margin.left + cWidth + margin.right;
          if (lRight > width) {
            left = padding.left;
            if (vPos != 0) {
              top += cHeight + margin.top;
            }
          }
        } else {
          lHeight = height - margin.top - margin.bottom;
        }
        left += margin.left;
        nLeft = left;
        nTop = top + margin.top;
        nHeight = lHeight;
        left += cWidth + margin.right;
      } else if (layout.layoutStyle == "righttoleft") {
        if (i == 0) {
          left = width - padding.left;
        }
        double lHeight = 0;
        if (layout.autoWrap) {
          lHeight = size.cy;
          double lLeft = left - margin.left - cWidth - margin.right;
          if (lLeft < padding.left) {
            left = width - padding.left;
            if (vPos != 0) {
              top += cHeight + margin.top;
            }
          }
        } else {
          lHeight = height - margin.top - margin.bottom;
        }
        left -= cWidth + margin.left;
        nLeft = left;
        nTop = top + margin.top;
        nHeight = lHeight;
      } else if (layout.layoutStyle == "toptobottom") {
        double lWidth = 0;
        if (layout.autoWrap) {
          lWidth = size.cx;
          double lBottom = top + margin.top + cHeight + margin.bottom;
          if (lBottom > height) {
            if (vPos != 0) {
              left += cWidth + margin.left + margin.right;
            }
            top = padding.top;
          }
        } else {
          lWidth = width - margin.left - margin.right;
        }
        top += margin.top;
        nTop = top;
        nLeft = left + margin.left;
        nWidth = lWidth;
        top += cHeight + margin.bottom;
      }
      if (cLeft != nLeft ||
          cTop != nTop ||
          cWidth != nWidth ||
          cHeight != nHeight) {
        view.location = FCPoint(nLeft, nTop);
        view.size = FCSize(nWidth, nHeight);
        reset = true;
      }
      vPos++;
    }
  }
  return reset;
}

/*
* 重置分割线的布局
* split:分割视图
*/
bool resetSplitLayoutDiv(FCSplitLayoutDiv split) {
  bool reset = false;
  FCRect splitRect = FCRect(0, 0, 0, 0);
  double width = split.size.cx, height = split.size.cy;
  FCRect fRect = FCRect(0, 0, 0, 0);
  FCRect sRect = FCRect(0, 0, 0, 0);
  FCView splitter = split.getSplitter();
  FCSize splitterSize = FCSize(0, 0);
  if (splitter.visible) {
    splitterSize.cx = splitter.size.cx;
    splitterSize.cy = splitter.size.cy;
  }
  String layoutStyle = split.layoutStyle;
  if (layoutStyle == "bottomtotop") {
    if (split.splitMode == "absolutesize" || split.oldSize.cy == 0) {
      splitRect.left = 0;
      splitRect.top = height - (split.oldSize.cy - splitter.location.y);
      splitRect.right = width;
      splitRect.bottom = splitRect.top + splitterSize.cy;
    } else if (split.splitMode == "percentsize") {
      splitRect.left = 0;
      if (split.splitPercent == -1) {
        split.splitPercent = splitter.location.y / split.oldSize.cy;
      }
      splitRect.top = height * split.splitPercent;
      splitRect.right = width;
      splitRect.bottom = splitRect.top + splitterSize.cy;
    }
    fRect.left = 0;
    fRect.top = splitRect.bottom;
    fRect.right = width;
    fRect.bottom = height;
    sRect.left = 0;
    sRect.top = 0;
    sRect.right = width;
    sRect.bottom = splitRect.top;
  } else if (layoutStyle == "lefttoright") {
    if (split.splitMode == "absolutesize" || split.oldSize.cx == 0) {
      splitRect.left = splitter.location.x;
      splitRect.top = 0;
      splitRect.right = splitRect.left + splitterSize.cx;
      splitRect.bottom = height;
    } else if (split.splitMode == "percentsize") {
      if (split.splitPercent == -1) {
        split.splitPercent = splitter.location.x / split.oldSize.cx;
      }
      splitRect.left = width * split.splitPercent;
      splitRect.top = 0;
      splitRect.right = splitRect.left + splitterSize.cx;
      splitRect.bottom = height;
    }
    fRect.left = 0;
    fRect.top = 0;
    fRect.right = splitRect.left;
    fRect.bottom = height;
    sRect.left = splitRect.right;
    sRect.top = 0;
    sRect.right = width;
    sRect.bottom = height;
  } else if (layoutStyle == "righttoleft") {
    if (split.splitMode == "absolutesize" || split.oldSize.cx == 0) {
      splitRect.left = width - (split.oldSize.cx - splitter.location.x);
      splitRect.top = 0;
      splitRect.right = splitRect.left + splitterSize.cx;
      splitRect.bottom = height;
    } else if (split.splitMode == "percentsize") {
      if (split.splitPercent == -1) {
        split.splitPercent = splitter.location.x / split.oldSize.cx;
      }
      splitRect.left = width * split.splitPercent;
      splitRect.top = 0;
      splitRect.right = splitRect.left + splitterSize.cx;
      splitRect.bottom = height;
    }
    fRect.left = splitRect.right;
    fRect.top = 0;
    fRect.right = width;
    fRect.bottom = height;
    sRect.left = 0;
    sRect.top = 0;
    sRect.right = splitRect.left;
    sRect.bottom = height;
  } else if (layoutStyle == "toptobottom") {
    if (split.splitMode == "absolutesize" || split.oldSize.cy == 0) {
      splitRect.left = 0;
      splitRect.top = splitter.location.y;
      splitRect.right = width;
      splitRect.bottom = splitRect.top + splitterSize.cy;
    } else if (split.splitMode == "percentsize") {
      splitRect.left = 0;
      if (split.splitPercent == -1) {
        split.splitPercent = splitter.location.y / split.oldSize.cy;
      }
      splitRect.top = height * split.splitPercent;
      splitRect.right = width;
      splitRect.bottom = splitRect.top + splitterSize.cy;
    }
    fRect.left = 0;
    fRect.top = 0;
    fRect.right = width;
    fRect.bottom = splitRect.top;
    sRect.left = 0;
    sRect.top = splitRect.bottom;
    sRect.right = width;
    sRect.bottom = height;
  }
  if (splitter.visible) {
    FCRect spRect = FCRect(
        splitter.location.x,
        splitter.location.y,
        splitter.location.x + splitter.size.cx,
        splitter.location.y + splitter.size.cy);
    if (spRect.left != splitRect.left ||
        spRect.top != splitRect.top ||
        spRect.right != splitRect.right ||
        spRect.bottom != splitRect.bottom) {
      splitter.location = FCPoint(splitRect.left, splitRect.top);
      splitter.size = FCSize(
          splitRect.right - splitRect.left, splitRect.bottom - splitRect.top);
      reset = true;
    }
  }
  FCView firstView = split.getFirstView();
  FCRect fcRect = FCRect(
      firstView.location.x,
      firstView.location.y,
      firstView.location.x + firstView.size.cx,
      firstView.location.y + firstView.size.cy);
  if (fcRect.left != fRect.left ||
      fcRect.top != fRect.top ||
      fcRect.right != fRect.right ||
      fcRect.bottom != fRect.bottom) {
    reset = true;
    firstView.location = FCPoint(fRect.left, fRect.top);
    firstView.size = FCSize(fRect.right - fRect.left, fRect.bottom - fRect.top);
  }
  FCView secondView = split.getSecondView();
  FCRect scRect = FCRect(
      secondView.location.x,
      secondView.location.y,
      secondView.location.x + secondView.size.cx,
      secondView.location.y + secondView.size.cy);
  if (scRect.left != sRect.left ||
      scRect.top != sRect.top ||
      scRect.right != sRect.right ||
      scRect.bottom != sRect.bottom) {
    reset = true;
    secondView.location = FCPoint(sRect.left, sRect.top);
    secondView.size =
        FCSize(sRect.right - sRect.left, sRect.bottom - sRect.top);
  }
  split.oldSize = FCSize(width, height);
  return reset;
}

/*
* 更新页的布局
* tabView:多页夹
* tabPage:页
* left:左侧坐标
* top:上方坐标
* width:宽度
* height:高度
* tw:页头按钮的宽度
* th:页头按钮的高度
*/
void updataPageLayout(FCTabView tabView, FCTabPage tabPage, double left,
    double top, double width, double height, double tw, double th) {
  FCRect newBounds = FCRect(0, 0, 0, 0);
  if (tabView.layout == "bottom") {
    newBounds.left = 0;
    newBounds.top = 0;
    newBounds.right = width;
    newBounds.bottom = height - th;
    tabPage.getHeaderButton().location = FCPoint(left, height - th);
  } else if (tabView.layout == "left") {
    newBounds.left = tw;
    newBounds.top = 0;
    newBounds.right = width;
    newBounds.bottom = height;
    tabPage.getHeaderButton().location = FCPoint(0, top);
  } else if (tabView.layout == "right") {
    newBounds.left = 0;
    newBounds.top = 0;
    newBounds.right = width - tw;
    newBounds.bottom = height;
    tabPage.getHeaderButton().location = FCPoint(width - tw, top);
  } else if (tabView.layout == "top") {
    newBounds.left = 0;
    newBounds.top = th;
    newBounds.right = width;
    newBounds.bottom = height;
    tabPage.getHeaderButton().location = FCPoint(left, 0);
  }
  tabPage.location = FCPoint(newBounds.left, newBounds.top);
  tabPage.size = FCSize(
      newBounds.right - newBounds.left, newBounds.bottom - newBounds.top);
}

/*
* 更新多页夹的布局
* tabView:多页夹
*/
void updateTabLayout(FCTabView tabView) {
  double width = tabView.size.cx;
  double height = tabView.size.cy;
  double left = 0, top = 0;
  for (int i = 0; i < tabView.tabPages.length; i++) {
    FCTabPage tabPage = tabView.tabPages[i];
    FCView headerButton = tabView.tabPages[i].getHeaderButton();
    if (headerButton.visible) {
      double tw = headerButton.size.cx;
      double th = headerButton.size.cy;
      updataPageLayout(tabView, tabPage, left, top, width, height, tw, th);
      left += tw;
      top += th;
    } else {
      tabPage.visible = false;
    }
  }
}

/*
* 添加页
* tabView:多页夹
* tabPage:页
* tabButton:页头按钮
*/
void addTabPage(FCTabView tabView, FCTabPage tabPage, FCButton tabButton) {
  tabPage.setHeaderButton(tabButton);
  tabPage.setParent(tabView);
  tabPage.setPaint(tabView.getPaint());
  tabButton.setParent(tabView);
  tabButton.setPaint(tabView.getPaint());
  tabView.tabPages.add(tabPage);
  tabView.views.add(tabPage);
  tabView.views.add(tabButton);
}

/*
* 移除页
* tabView:多页夹
* tabPage:页
*/
void removeTabPage(FCTabView tabView, FCTabPage tabPage) {
  FCView headerButton = tabPage.getHeaderButton();
  for (int i = 0; i < tabView.views.length; i++) {
    if (tabView.views[i] == headerButton) {
      tabView.views.removeAt(i);
      break;
    }
  }
  for (int i = 0; i < tabView.views.length; i++) {
    if (tabView.views[i] == tabPage) {
      tabView.views.removeAt(i);
      break;
    }
  }
  for (int i = 0; i < tabView.tabPages.length; i++) {
    if (tabView.tabPages[i] == tabPage) {
      tabView.tabPages.removeAt(i);
      break;
    }
  }
}

/*
* 选中页
* tabView:多页夹
* tabPage:页
*/
void selectTabPage(FCTabView tabView, FCTabPage tabPage) {
  for (int i = 0; i < tabView.tabPages.length; i++) {
    FCTabPage tp = tabView.tabPages[i];
    if (tp == tabPage) {
      tp.visible = true;
    } else {
      tp.visible = false;
    }
  }
  updateTabLayout(tabView);
}

/*
* 重绘多页加
* tabView:多页夹
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawTabViewBorder(FCTabView tabView, FCPaint paint, FCRect clipRect) {
  if (tabView.underLineColor != "none") {
    for (int i = 0; i < tabView.tabPages.length; i++) {
      FCTabPage tp = tabView.tabPages[i];
      if (tp.visible) {
        FCView headerButton = tp.getHeaderButton();
        FCPoint location =
            FCPoint(headerButton.location.x, headerButton.location.y);
        FCSize size = headerButton.size;
        if (tabView.useAnimation) {
          if (tabView.underPoint.x != 0 && tabView.underPoint.y != 0) {
            location.x = tabView.underPoint.x;
            location.y = tabView.underPoint.y;
          }
        }
        if (tabView.layout == "bottom") {
          paint.fillRect(tabView.underLineColor, location.x, location.y,
              location.x + size.cx, location.y + tabView.underLineSize);
        } else if (tabView.layout == "left") {
          paint.fillRect(
              tabView.underLineColor,
              location.x + size.cx - tabView.underLineSize,
              location.y,
              location.x + size.cx,
              location.y + size.cy);
        } else if (tabView.layout == "top") {
          paint.fillRect(
              tabView.underLineColor,
              location.x,
              location.y + size.cy - tabView.underLineSize,
              location.x + size.cx,
              location.y + size.cy);
        } else if (tabView.layout == "right") {
          paint.fillRect(tabView.underLineColor, location.x, location.y,
              location.x + tabView.underLineSize, location.y + size.cy);
        }
        break;
      }
    }
  }
}

/*
* 获取总的偏移量
* node:树节点
*/
double getTotalIndent(FCTreeNode node) {
  if (node.getParentNode().isNotEmpty) {
    return node.indent + getTotalIndent(node.getParentNode());
  } else {
    return node.indent;
  }
}

/*
* 绘制单元格
* tree:树
* row:行
* column:列
* node:节点
* paint:绘图对象
* left:左侧坐标
* top:上方坐标
* right:右侧坐标
* bottom:下方坐标
*/
void drawTreeNode(
    FCTree tree,
    FCTreeRow row,
    FCTreeColumn column,
    FCTreeNode node,
    FCPaint paint,
    double left,
    double top,
    double right,
    double bottom) {
  if (node.backColor != "none") {
    paint.fillRect(node.backColor, left, top, right, bottom);
  }
  if (node.value.isNotEmpty) {
    FCSize tSize = paint.textSize(node.value, node.font);
    double tLeft = left + 2 + getTotalIndent(node);

    double wLeft = tLeft;
    double cR = tree.checkBoxWidth / 3;
    if (tree.showCheckBox) {
      wLeft += tree.checkBoxWidth;
      if (node.checked) {
        paint.fillRect(
            node.textColor,
            tLeft + (tree.checkBoxWidth - cR) / 2,
            top + (tree.rowHeight - cR) / 2,
            tLeft + (tree.checkBoxWidth + cR) / 2,
            top + (tree.rowHeight + cR) / 2);
      } else {
        paint.drawRect(
            node.textColor,
            1,
            0,
            tLeft + (tree.checkBoxWidth - cR) / 2,
            top + (tree.rowHeight - cR) / 2,
            tLeft + (tree.checkBoxWidth + cR) / 2,
            top + (tree.rowHeight + cR) / 2);
      }
    }
    if (node.childNodes.isNotEmpty) {
      paint.beginPath();
      if (node.collapsed) {
        paint.addLine(
            wLeft + (tree.collapsedWidth + cR) / 2,
            top + tree.rowHeight / 2,
            wLeft + (tree.collapsedWidth - cR) / 2,
            top + (tree.rowHeight - cR) / 2);
        paint.addLine(
            wLeft + (tree.collapsedWidth - cR) / 2,
            top + (tree.rowHeight - cR) / 2,
            wLeft + (tree.collapsedWidth - cR) / 2,
            top + (tree.rowHeight + cR) / 2);
        paint.addLine(
            wLeft + (tree.collapsedWidth - cR) / 2,
            top + (tree.rowHeight + cR) / 2,
            wLeft + (tree.collapsedWidth + cR) / 2,
            top + tree.rowHeight / 2);
      } else {
        paint.addLine(
            wLeft + (tree.collapsedWidth - cR) / 2,
            top + (tree.rowHeight - cR) / 2,
            wLeft + (tree.collapsedWidth + cR) / 2,
            top + (tree.rowHeight - cR) / 2);
        paint.addLine(
            wLeft + (tree.collapsedWidth + cR) / 2,
            top + (tree.rowHeight - cR) / 2,
            wLeft + tree.collapsedWidth / 2,
            top + (tree.rowHeight + cR) / 2);
        paint.addLine(
            wLeft + tree.collapsedWidth / 2,
            top + (tree.rowHeight + cR) / 2,
            wLeft + (tree.collapsedWidth - cR) / 2,
            top + (tree.rowHeight - cR) / 2);
      }
      paint.fillPath(node.textColor);
      paint.closePath();
      wLeft += tree.collapsedWidth;
    }
    if (tSize.cx > column.width) {
      paint.drawTextAutoEllipsis(
          node.value,
          node.textColor,
          node.font,
          wLeft,
          top + tree.rowHeight / 2 - tSize.cy / 2,
          wLeft + column.width,
          top + tree.rowHeight / 2 - tSize.cy / 2);
    } else {
      paint.drawText(node.value, node.textColor, node.font, wLeft,
          top + tree.rowHeight / 2 - tSize.cy / 2);
    }
  }
}

/*
* 绘制树
* tree:树
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawTree(FCTree tree, FCPaint paint, FCRect clipRect) {
  double cTop = -tree.scrollV + tree.headerHeight;
  //绘制行
  double colLeft = 0;
  for (int i = 0; i < tree.columns.length; i++) {
    if (tree.columns[i].widthStr.isNotEmpty) {
      String newWidthStr = tree.columns[i].widthStr.replaceAll("%", "");
      tree.columns[i].width = double.parse(newWidthStr) * tree.size.cx / 100;
    }
    FCRect colRect =
        FCRect(colLeft, 0, colLeft + tree.columns[i].width, tree.headerHeight);
    tree.columns[i].bounds = colRect;
    tree.columns[i].index = i;
    colLeft += tree.columns[i].width;
  }
  updateTreeRowIndex(tree);
  for (int i = 0; i < tree.rows.length; i++) {
    FCTreeRow row = tree.rows[i];
    if (row.visible) {
      double rTop = cTop, rBottom = cTop + tree.rowHeight;
      if (rBottom >= 0 && cTop <= tree.size.cy) {
        for (int j = 0; j < row.cells.length; j++) {
          FCTreeNode node = row.cells[j];
          FCTreeColumn treeColumn = node.getColumn();
          if (!treeColumn.isNotEmpty) {
            treeColumn = tree.columns[j];
          }
          if (treeColumn.visible) {
            double nodeWidth = treeColumn.width;
            double nodeHeight = tree.rowHeight;
            FCRect cRect = FCRect(
                treeColumn.bounds.left - tree.scrollH,
                rTop,
                treeColumn.bounds.left + nodeWidth - tree.scrollH,
                rTop + nodeHeight);
            if (cRect.right >= 0 && cRect.left < tree.size.cx) {
              if (paint.hasPaintTreeNodeEvent) {
                paint.onPaintTreeNode(tree, row, treeColumn, node, paint,
                    cRect.left, cRect.top, cRect.right, cRect.bottom);
              } else {
                drawTreeNode(tree, row, treeColumn, node, paint, cRect.left,
                    cRect.top, cRect.right, cRect.bottom);
              }
            }
          }
        }
      }
      if (cTop > tree.size.cy) {
        break;
      }
      cTop += tree.rowHeight;
    }
  }
}

/*
* 绘制滚动条
* tree:树
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawTreeScrollBar(FCTree tree, FCPaint paint, FCRect clipRect) {
  tree.hScrollIsVisible = false;
  tree.vScrollIsVisible = false;
  if (tree.showHScrollBar) {
    double contentWidth = getTreeContentWidth(tree);
    if (contentWidth > 0 && contentWidth > tree.size.cx) {
      double sLeft = tree.scrollH / contentWidth * tree.size.cx;
      double sRight =
          (tree.scrollH + tree.size.cx) / contentWidth * tree.size.cx;
      if (sRight - sLeft < tree.scrollSize) {
        sRight = sLeft + tree.scrollSize;
      }
      if (tree.getPaint().getTouchMoveView() == tree &&
          (tree.hoverScrollHButton || tree.downScrollHButton)) {
        paint.fillRect(tree.scrollBarHoveredColor, sLeft,
            tree.size.cy - tree.scrollSize, sRight, tree.size.cy);
      } else {
        paint.fillRect(tree.scrollBarColor, sLeft,
            tree.size.cy - tree.scrollSize, sRight, tree.size.cy);
      }
      tree.hScrollIsVisible = true;
    }
  }
  if (tree.showVScrollBar) {
    double contentHeight = getTreeContentHeight(tree);
    if (contentHeight > 0 && contentHeight > tree.size.cy) {
      double sTop = tree.headerHeight +
          tree.scrollV /
              contentHeight *
              (tree.size.cy - tree.headerHeight - tree.scrollSize);
      double sBottom = sTop +
          ((tree.size.cy - tree.headerHeight)) /
              contentHeight *
              (tree.size.cy - tree.headerHeight - tree.scrollSize);
      if (sBottom - sTop < tree.scrollSize) {
        sBottom = sTop + tree.scrollSize;
      }
      if (tree.getPaint().getTouchMoveView() == tree &&
          (tree.hoverScrollVButton || tree.downScrollVButton)) {
        paint.fillRect(tree.scrollBarHoveredColor,
            tree.size.cx - tree.scrollSize, sTop, tree.size.cx, sBottom);
      } else {
        paint.fillRect(tree.scrollBarColor, tree.size.cx - tree.scrollSize,
            sTop, tree.size.cx, sBottom);
      }
      tree.vScrollIsVisible = true;
    }
  }
}

/*
* 获取内容的宽度
* tree:树
*/
double getTreeContentWidth(FCTree tree) {
  double cWidth = 0;
  for (int i = 0; i < tree.columns.length; i++) {
    if (tree.columns[i].visible) {
      cWidth += tree.columns[i].width;
    }
  }
  return cWidth;
}

/*
* 获取内容的高度
* tree:树
*/
double getTreeContentHeight(FCTree tree) {
  double cHeight = 0;
  for (int i = 0; i < tree.rows.length; i++) {
    if (tree.rows[i].visible) {
      cHeight += tree.rowHeight;
    }
  }
  return cHeight;
}

/*
* 树的鼠标移动方法
* tree: 树
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
*/
void touchMoveTree(FCTree tree, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint) {
  tree.hoverScrollHButton = false;
  tree.hoverScrollVButton = false;
  FCPoint mp = firstPoint;
  if (firstTouch) {
    if (tree.showHScrollBar || tree.showVScrollBar) {
      if (tree.downScrollHButton) {
        double contentWidth = getTreeContentWidth(tree);
        double subX = (mp.x - tree.startPoint.x) / tree.size.cx * contentWidth;
        double newScrollH = tree.startScrollH + subX;
        if (newScrollH < 0) {
          newScrollH = 0;
        } else if (newScrollH > contentWidth - tree.size.cx) {
          newScrollH = contentWidth - tree.size.cx;
        }
        tree.scrollH = newScrollH;
        tree.getPaint().cancelClick = true;
        return;
      } else if (tree.downScrollVButton) {
        double contentHeight = getTreeContentHeight(tree);
        double subY = (mp.y - tree.startPoint.y) /
            (tree.size.cy - tree.headerHeight - tree.scrollSize) *
            contentHeight;
        double newScrollV = tree.startScrollV + subY;
        if (newScrollV < 0) {
          newScrollV = 0;
        } else if (newScrollV >
            contentHeight -
                (tree.size.cy - tree.headerHeight - tree.scrollSize)) {
          newScrollV = contentHeight -
              (tree.size.cy - tree.headerHeight - tree.scrollSize);
        }
        tree.scrollV = newScrollV;
        tree.getPaint().cancelClick = true;
        return;
      }
    }
    if (tree.allowDragScroll) {
      double contentWidth = getTreeContentWidth(tree);
      if (contentWidth > tree.size.cx) {
        double subX = tree.startPoint.x - mp.x;
        double newScrollH = tree.startScrollH + subX;
        if (newScrollH < 0) {
          newScrollH = 0;
        } else if (newScrollH > contentWidth - tree.size.cx) {
          newScrollH = contentWidth - tree.size.cx;
        }
        tree.scrollH = newScrollH;
        if (subX > 5 || subX < -5) {
          tree.getPaint().cancelClick = true;
        }
      }
      double contentHeight = getTreeContentHeight(tree);
      if (contentHeight > tree.size.cy) {
        double subY = tree.startPoint.y - mp.y;
        double newScrollV = tree.startScrollV + subY;
        if (newScrollV < 0) {
          newScrollV = 0;
        } else if (newScrollV >
            contentHeight -
                (tree.size.cy - tree.headerHeight - tree.scrollSize)) {
          newScrollV = contentHeight -
              (tree.size.cy - tree.headerHeight - tree.scrollSize);
        }
        tree.scrollV = newScrollV;
        if (subY > 5 || subY < -5) {
          tree.getPaint().cancelClick = true;
        }
      }
    }
  } else {
    if (tree.showHScrollBar) {
      double contentWidth = getTreeContentWidth(tree);
      if (contentWidth > 0 && contentWidth > tree.size.cx) {
        double sLeft = tree.scrollH / contentWidth * tree.size.cx;
        double sRight =
            (tree.scrollH + tree.size.cx) / contentWidth * tree.size.cx;
        if (sRight - sLeft < tree.scrollSize) {
          sRight = sLeft + tree.scrollSize;
        }
        if (mp.x >= sLeft &&
            mp.x <= sRight &&
            mp.y >= tree.size.cy - tree.scrollSize &&
            mp.y <= tree.size.cy) {
          tree.hoverScrollHButton = true;
          return;
        } else {
          tree.hoverScrollHButton = false;
        }
      }
    }
    if (tree.showVScrollBar) {
      double contentHeight = getTreeContentHeight(tree);
      if (contentHeight > 0 && contentHeight > tree.size.cy) {
        double sTop = tree.headerHeight +
            tree.scrollV /
                contentHeight *
                (tree.size.cy - tree.headerHeight - tree.scrollSize);
        double sBottom = tree.headerHeight +
            (tree.scrollV +
                    (tree.size.cy - tree.headerHeight - tree.scrollSize)) /
                contentHeight *
                (tree.size.cy - tree.headerHeight - tree.scrollSize);
        if (sBottom - sTop < tree.scrollSize) {
          sBottom = sTop + tree.scrollSize;
        }
        if (mp.x >= tree.size.cx - tree.scrollSize &&
            mp.x <= tree.size.cx &&
            mp.y >= sTop &&
            mp.y <= sBottom) {
          tree.hoverScrollVButton = true;
          return;
        } else {
          tree.hoverScrollVButton = false;
        }
      }
    }
  }
}

/*
* 树的鼠标按下方法
* tree: 树
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
* clicks:点击次数
*/
void touchDownTree(FCTree tree, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint, int clicks) {
  FCPoint mp = firstPoint;
  tree.startPoint = mp;
  tree.touchDownTime = getTickCount();
  tree.downScrollHButton = false;
  tree.downScrollVButton = false;
  tree.scrollAddSpeed = 0;
  tree.isScrolling = false;
  if (tree.showHScrollBar) {
    double contentWidth = getTreeContentWidth(tree);
    if (contentWidth > 0 && contentWidth > tree.size.cx) {
      double sLeft = tree.scrollH / contentWidth * tree.size.cx;
      double sRight =
          (tree.scrollH + tree.size.cx) / contentWidth * tree.size.cx;
      if (sRight - sLeft < tree.scrollSize) {
        sRight = sLeft + tree.scrollSize;
      }
      if (mp.x >= sLeft &&
          mp.x <= sRight &&
          mp.y >= tree.size.cy - tree.scrollSize &&
          mp.y <= tree.size.cy) {
        tree.downScrollHButton = true;
        tree.startScrollH = tree.scrollH;
        return;
      }
    }
  }
  if (tree.showVScrollBar) {
    double contentHeight = getTreeContentHeight(tree);
    if (contentHeight > 0 && contentHeight > tree.size.cy) {
      double sTop = tree.headerHeight +
          tree.scrollV /
              contentHeight *
              (tree.size.cy - tree.headerHeight - tree.scrollSize);
      double sBottom = tree.headerHeight +
          (tree.scrollV +
                  (tree.size.cy - tree.headerHeight - tree.scrollSize)) /
              contentHeight *
              (tree.size.cy - tree.headerHeight - tree.scrollSize);
      if (sBottom - sTop < tree.scrollSize) {
        sBottom = sTop + tree.scrollSize;
      }
      if (mp.x >= tree.size.cx - tree.scrollSize &&
          mp.x <= tree.size.cx &&
          mp.y >= sTop &&
          mp.y <= sBottom) {
        tree.downScrollVButton = true;
        tree.startScrollV = tree.scrollV;
        return;
      }
    }
  }
  if (tree.allowDragScroll) {
    tree.startScrollH = tree.scrollH;
    tree.startScrollV = tree.scrollV;
  }

  double cLeft = -tree.scrollH;
  double cTop = -tree.scrollV + tree.headerHeight;
  for (int i = 0; i < tree.rows.length; i++) {
    FCTreeRow row = tree.rows[i];
    if (row.visible) {
      if (firstPoint.y >= cTop && firstPoint.y <= cTop + tree.rowHeight) {
        FCTreeNode node = row.cells[0];
        double tLeft = cLeft + 2 + getTotalIndent(node);
        double wLeft = tLeft;
        if (tree.showCheckBox) {
          wLeft += tree.checkBoxWidth;
          if (firstPoint.x < wLeft) {
            checkOrUnCheckTreeNode(node, !node.checked);
            invalidateView(tree);
            break;
          }
        }
        if (node.childNodes.isNotEmpty) {
          wLeft += tree.collapsedWidth;
          if (firstPoint.x < wLeft) {
            node.collapsed = !node.collapsed;
            hideOrShowTreeNode(node, !node.collapsed);
            invalidateView(tree);
            break;
          }
        }
        if (tree.getPaint().hasClickTreeNodeEvent) {
          tree.getPaint().onClickTreeNode(
              tree, node, firstTouch, firstPoint, secondTouch, secondPoint, clicks);
        }
      }
      cTop += tree.rowHeight;
    }
  }
}

/*
* 表格的鼠标抬起方法
* tree: 树
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
* clicks:点击次数
*/
void touchUpTree(FCTree tree, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint, int clicks) {
  tree.hoverScrollHButton = false;
  tree.hoverScrollVButton = false;
  if (firstTouch && !tree.downScrollHButton && !tree.downScrollVButton) {
    if (tree.allowDragScroll) {
      int touchUpTime = getTickCount();
      int diff = (touchUpTime - tree.touchDownTime) ~/ 1000;
      //加速滚动
      if (diff > 0 && diff < 500) {
        int sub1 =
            (30 * (((firstPoint.y - tree.startPoint.y).abs()) / 20) / diff * 10)
                .toInt();
        int sub2 =
            (30 * (((firstPoint.x - tree.startPoint.x).abs()) / 20) / diff * 10)
                .toInt();
        if (sub1.abs() > sub2.abs()) {
          if (firstPoint.y < tree.startPoint.y) {
            tree.scrollAddSpeed += sub1;
          } else {
            tree.scrollAddSpeed -= sub1;
          }
          tree.scrollDirection = 0;
        } else {
          if (firstPoint.x < tree.startPoint.x) {
            tree.scrollAddSpeed += sub2;
          } else {
            tree.scrollAddSpeed -= sub2;
          }
          tree.scrollDirection = 1;
        }
        tree.isScrolling = true;
        if ((tree.scrollAddSpeed).abs() > 0) {
          tree.getPaint().cancelClick = true;
        }
      }
    }
  }
  tree.downScrollHButton = false;
  tree.downScrollVButton = false;
}

/*
* 更新行的索引
* tree:树
*/
void updateTreeRowIndex(FCTree tree) {
  for (int i = 0; i < tree.rows.length; i++) {
    tree.rows[i].index = i;
  }
}

/*
* 获取最后一行的索引 
* node:树节点
*/
int getTreeLastNodeRowIndex(FCTreeNode node) {
  int rowIndex = node.getRow().index;
  for (int i = 0; i < node.childNodes.length; i++) {
    int rIndex = getTreeLastNodeRowIndex(node.childNodes[i]);
    if (rowIndex < rIndex) {
      rowIndex = rIndex;
    }
  }
  return rowIndex;
}

/*
  * 添加节点
  * tree:树
  * node:要添加的节点
  * parentNode:父节点
  */
void appendTreeNode(FCTree tree, FCTreeNode node, FCTreeNode parentNode) {
  if (!parentNode.isNotEmpty) {
    FCTreeRow newRow = FCTreeRow();
    tree.rows.add(newRow);
    node.setRow(newRow);
    newRow.cells.add(node);
    tree.childNodes.add(node);
  } else {
    FCTreeRow newRow = FCTreeRow();
    if (!parentNode.childNodes.isNotEmpty) {
      tree.rows.insert(parentNode.getRow().index + 1, newRow);
    } else {
      tree.rows.insert(getTreeLastNodeRowIndex(parentNode) + 1, newRow);
    }
    node.setParentNode(parentNode);
    node.indent = tree.indent;
    node.setRow(newRow);
    newRow.cells.add(node);
    parentNode.childNodes.add(node);
    if (parentNode.collapsed) {
      newRow.visible = false;
    }
  }
  updateTreeRowIndex(tree);
}

/*
* 移除节点
* tree:树
* node:要添加的节点
*/
void removeTreeNode(FCTree tree, FCTreeNode node) {
  //移除相互关系
  if (!node.isNotEmpty) {
    int nodesSize = tree.childNodes.length;
    for (int i = 0; i < nodesSize; i++) {
      if (tree.childNodes[i] == node) {
        tree.childNodes.removeAt(1);
        break;
      }
    }
  } else {
    int nodesSize = node.getParentNode().childNodes.length;
    for (int i = 0; i < nodesSize; i++) {
      if (node.getParentNode().childNodes[i] == node) {
        node.getParentNode().childNodes.removeAt(i);
        break;
      }
    }
  }
  //移除行
  tree.rows.removeAt(node.getRow().index);
  updateTreeRowIndex(tree);
}

/*
* 展开或折叠节点
* node:节点
* visible:是否可见
*/
void hideOrShowTreeNode(FCTreeNode node, bool visible) {
  if (node.childNodes.isNotEmpty) {
    for (int i = 0; i < node.childNodes.length; i++) {
      node.childNodes[i].getRow().visible = visible;
      hideOrShowTreeNode(node.childNodes[i], visible);
    }
  }
}

/*
* 展开或折叠节点
* node:节点
* checked:是否选中
*/
void checkOrUnCheckTreeNode(FCTreeNode node, bool checked) {
  node.checked = checked;
  if (node.childNodes.isNotEmpty) {
    for (int i = 0; i < node.childNodes.length; i++) {
      checkOrUnCheckTreeNode(node.childNodes[i], checked);
    }
  }
}

/*
* 树的鼠标滚轮方法
* tree:树
* delta:滚轮值
*/
void touchWheelTree(FCTree tree, int delta) {
  double oldScrollV = tree.scrollV;
  if (delta > 0) {
    oldScrollV -= tree.rowHeight;
  } else if (delta < 0) {
    oldScrollV += tree.rowHeight;
  }
  double contentHeight = getTreeContentHeight(tree);
  if (contentHeight < tree.size.cy) {
    tree.scrollV = 0;
  } else {
    if (oldScrollV < 0) {
      oldScrollV = 0;
    } else if (oldScrollV >
        contentHeight - tree.size.cy + tree.headerHeight + tree.scrollSize) {
      oldScrollV =
          contentHeight - tree.size.cy + tree.headerHeight + tree.scrollSize;
    }
    tree.scrollV = oldScrollV;
  }
}

/*
* 检查树的滚动
* tree:树
*/
void checkTreeScroll(FCTree tree) {
  if (tree.isScrolling) {
    int sub = tree.scrollAddSpeed ~/ 10;
    if (sub == 0 && tree.scrollAddSpeed > 1) {
      sub = 1;
    } else if (sub == 0 && tree.scrollAddSpeed < -1) {
      sub = -1;
    }
    tree.scrollAddSpeed -= sub;
    if (sub.abs() <= 1) {
      tree.scrollAddSpeed = 0;
      tree.isScrolling = false;
      invalidateView(tree);
    } else {
      double oldScrollV = tree.scrollV + tree.scrollAddSpeed;
      double oldScrollH = tree.scrollH + tree.scrollAddSpeed;
      if (tree.scrollDirection == 0) {
        double contentHeight = getTreeContentHeight(tree);
        if (contentHeight < tree.size.cy) {
          tree.scrollV = 0;
        } else {
          if (oldScrollV < 0) {
            oldScrollV = 0;
          } else if (oldScrollV >
              contentHeight -
                  tree.size.cy +
                  tree.headerHeight +
                  tree.scrollSize) {
            oldScrollV = contentHeight -
                tree.size.cy +
                tree.headerHeight +
                tree.scrollSize;
          }
          tree.scrollV = oldScrollV;
        }
      } else {
        double contentWidth = getTreeContentHeight(tree);
        if (contentWidth < tree.size.cx) {
          tree.scrollH = 0;
        } else {
          if (oldScrollH < 0) {
            oldScrollH = 0;
          } else if (oldScrollH > contentWidth - tree.size.cx) {
            oldScrollH = contentWidth - tree.size.cx;
          }
          tree.scrollH = oldScrollH;
        }
      }
      invalidateView(tree);
    }
  }
}

/*
* 字符转换为颜色
* str:字符串
*/
Color stringToColor(String str) {
  if (str.contains("rgb(")) {
    String newStr = str.substring(4, str.length - 1);
    List<String> strs = newStr.split(",");
    return Color.fromARGB(
        255, int.parse(strs[0]), int.parse(strs[1]), int.parse(strs[2]));
  } else if (str.contains("rgba(")) {
    String newStr = str.substring(5, str.length - 1);
    List<String> strs = newStr.split(",");
    return Color.fromARGB(int.parse(strs[3]), int.parse(strs[0]),
        int.parse(strs[1]), int.parse(strs[2]));
  }
  return noneColor;
}

/*
* 获取系统毫秒数
*/
int getTickCount() {
  return DateTime.now().microsecondsSinceEpoch;
}

/*
 * 自动适应位置和大小
 * menu:菜单
 */
void adjustMenu(FCMenu menu) {
  resetLayoutDiv(menu);
  if (menu.autoSize) {
    double contentHeight = getDivContentHeight(menu);
    double maximumHeight = menu.maximumSize.cy;
    menu.size.cy = min(contentHeight, maximumHeight);
  }
  FCPoint mPoint = FCPoint(menu.location.x, menu.location.y);
  FCSize mSize = menu.size;
  FCPaint paint = menu.getPaint();
  FCSize nSize = FCSize(paint.size.cx / paint.scaleFactorX, paint.size.cy / paint.scaleFactorY);
  if (mPoint.x < 0) {
    mPoint.x = 0;
  }
  if (mPoint.y < 0) {
    mPoint.y = 0;
  }
  if (mPoint.x + mSize.cx > nSize.cx) {
    mPoint.x = nSize.cx - mSize.cx;
  }
  if (mPoint.y + mSize.cy > nSize.cy) {
    mPoint.y = nSize.cy - mSize.cy;
  }
  menu.location = mPoint;
  menu.scrollV = 0;
}

/*
 * 添加菜单项
 * item:菜单项
 * menu:菜单
 */
void addMenuItem(FCMenuItem item, FCMenu menu) {
  addViewToParent(item, menu);
  item.setParentMenu(menu);
  menu.items.add(item);
}

/*
 * 添加菜单项
 * item:菜单项
 * parentItem:父菜单项
 */
void addMenuItemToParent(FCMenuItem item, FCMenuItem parentItem) {
  item.setParentItem(parentItem);
  if (!parentItem.getDropDownMenu().isNotEmpty) {
    FCMenu newMenu = FCMenu();
    parentItem.setDropDownMenu(newMenu);
    addView(parentItem.getDropDownMenu(), parentItem.getPaint());
  }
  item.setParentMenu(parentItem.getDropDownMenu());
  addViewToParent(item, parentItem.getDropDownMenu());
  parentItem.items.add(item);
  parentItem.getDropDownMenu().items.add(item);
}

/*
 * 控制菜单的显示隐藏
 * paint:绘图对象
 */
void checkShowMenu(FCPaint paint) {
  bool paintAll = false;
  bool clickItem = false;
  if (paint.views.isNotEmpty) {
    for (int i = 0; i < paint.views.length; i++) {
      FCView view = paint.views[i];
      if (view.viewType == "menu") {
        FCMenu menu = view as FCMenu;
        if (view.visible) {
          if (view == paint.getTouchDownView()) {
            clickItem = true;
          }
          if (menu.items.isNotEmpty) {
            for (int j = 0; j < menu.items.length; j++) {
              FCMenuItem item = menu.items[j];
              if (item == paint.getTouchDownView()) {
                clickItem = true;
                break;
              }
            }
          }
        }
      }
    }
    if (!clickItem) {
      for (int i = 0; i < paint.views.length; i++) {
        FCView view = paint.views[i];
        if (view.viewType == "menu") {
          view.visible = false;
          paintAll = true;
        }
      }
    }
  }
  if (paintAll) {
    invalidate(paint);
  }
}

/*
 * 关闭网格视图
 * items:菜单集合
 */
bool closeMenus(List<FCMenuItem> items) {
  int itemSize = items.length;
  var close = false;
  if (itemSize > 0) {
    for (int i = 0; i < itemSize; i++) {
      FCMenuItem item = items[i];
      List<FCMenuItem> subItems = item.items;
      if (closeMenus(subItems)) {
        close = true;
      }
      FCMenu dropDownMenu = item.getDropDownMenu();
      if (dropDownMenu.isNotEmpty && dropDownMenu.visible) {
        dropDownMenu.visible = false;
        close = true;
      }
    }
  }
  return close;
}

/*
 * 鼠标移动到菜单项
 * item 菜单项
 */
void touchMoveMenuItem(FCMenuItem item) {
  FCMenuItem parentItem = item.getParentItem();
  List<FCMenuItem> items = <FCMenuItem>[];
  if (parentItem.isNotEmpty) {
    if (parentItem.getDropDownMenu().isNotEmpty) {
      items = parentItem.getDropDownMenu().items;
    }
  } else {
    if (item.getParentMenu().isNotEmpty) {
      items = item.getParentMenu().items;
    }
  }
  closeMenus(items);
  if (item.items.isNotEmpty) {
    FCMenu dropDownMenu = item.getDropDownMenu();
    //获取位置和大小
    if (dropDownMenu.isNotEmpty && !dropDownMenu.visible) {
      String layoutStyle = dropDownMenu.layoutStyle;
      var location = FCPoint(clientX(item) + item.size.cx, clientY(item));
      if (layoutStyle == "lefttoright" || layoutStyle == "righttoleft") {
        location.x = clientX(item);
        location.y = clientY(item) + item.size.cy;
      }
      //设置弹出位置
      dropDownMenu.location = location;
      dropDownMenu.visible = true;
      adjustMenu(dropDownMenu);
      invalidate(item.getPaint());
      return;
    }
  }
  invalidate(item.getPaint());
}

/*
* 重绘按钮
* item:菜单项
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawMenuItem(FCMenuItem item, FCPaint paint, FCRect clipRect) {
  if (item == paint.getTouchDownView()) {
    if (item.pushedColor != "none") {
      paint.fillRect(item.pushedColor, 0, 0, item.size.cx, item.size.cy);
    } else {
      if (item.backColor != "none") {
        paint.fillRect(item.backColor, 0, 0, item.size.cx, item.size.cy);
      }
    }
  } else if (item == paint.getTouchMoveView()) {
    if (item.hoveredColor != "none") {
      paint.fillRect(item.hoveredColor, 0, 0, item.size.cx, item.size.cy);
    } else {
      if (item.backColor != "none") {
        paint.fillRect(item.backColor, 0, 0, item.size.cx, item.size.cy);
      }
    }
  } else if (item.backColor != "none") {
    paint.fillRect(item.backColor, 0, 0, item.size.cx, item.size.cy);
  }
  if (item.textColor != "none" && item.text.isNotEmpty) {
    FCSize tSize = paint.textSize(item.text, item.font);
    paint.drawText(item.text, item.textColor, item.font,
        (item.size.cx - tSize.cx) / 2, (item.size.cy - tSize.cy) / 2);
  }
  if (item.borderColor != "none") {
    paint.drawRect(item.borderColor, item.borderWidth, 0, 0, 0, item.size.cx, item.size.cy);
  }
  if (item.items.isNotEmpty) {
    double tR = 5.0;
    paint.beginPath();
    paint.addLine(item.size.cx - 2, item.size.cy / 2, item.size.cx - 2 - tR * 2,
        item.size.cy / 2 - tR);
    paint.addLine(item.size.cx - 2 - tR * 2, item.size.cy / 2 - tR,
        item.size.cx - 2 - tR * 2, item.size.cy / 2 + tR);
    paint.addLine(item.size.cx - 2 - tR * 2, item.size.cy / 2 + tR,
        item.size.cx - 2, item.size.cy / 2);
    paint.fillPath(item.textColor);
    paint.closePath();
  }
}

/*
 * 点击菜单项
 * item:菜单项
 */
void clickMenuItem(FCMenuItem item) {
  bool paintAll = false;
  if (item.getParentMenu().isNotEmpty) {
    if (item.getParentMenu().getComboBox().isNotEmpty) {
      int index = -1;
      if (item.getParentMenu().items.isNotEmpty) {
        for (int i = 0; i < item.getParentMenu().items.length; i++) {
          if (item.getParentMenu().items[i] == item) {
            index = i;
            break;
          }
        }
        item.getParentMenu().getComboBox().selectedIndex = index;
        item.getParentMenu().getComboBox().text =
            item.getParentMenu().items[index].text;
        paintAll = true;
      }
    }
  }
  if (item.items.isNotEmpty) {
    for (int i = 0; i < item.getPaint().views.length; i++) {
      FCView subView = item.getPaint().views[i];
      if (subView.viewType == "menu") {
        if (subView.visible) {
          subView.visible = false;
          paintAll = true;
        }
      }
    }
  }
  if (paintAll) {
    invalidate(item.getPaint());
  }
}

/*
* 重绘按钮
* comboBox:下拉列表
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawComboBox(FCComboBox comboBox, FCPaint paint, FCRect clipRect) {
  if (comboBox.backColor != "none") {
    paint.fillRect(
        comboBox.backColor, 0, 0, comboBox.size.cx, comboBox.size.cy);
  }
  if (comboBox.textColor != "none" && comboBox.text.isNotEmpty) {
    FCSize tSize = paint.textSize(comboBox.text, comboBox.font);
    paint.drawText(comboBox.text, comboBox.textColor, comboBox.font, 5,
        (comboBox.size.cy - tSize.cy) / 2);
  }
  if (comboBox.borderColor != "none") {
    paint.drawRect(
        comboBox.borderColor, comboBox.borderWidth, 0, 0, 0, comboBox.size.cx, comboBox.size.cy);
  }
  double tR = 5.0;
  paint.beginPath();
  paint.addLine(comboBox.size.cx - 5 - tR * 2, comboBox.size.cy / 2 - tR,
      comboBox.size.cx - 5, comboBox.size.cy / 2 - tR);
  paint.addLine(comboBox.size.cx - 5, comboBox.size.cy / 2 - tR,
      comboBox.size.cx - 5 - tR, comboBox.size.cy / 2 + tR);
  paint.addLine(comboBox.size.cx - 5 - tR, comboBox.size.cy / 2 + tR,
      comboBox.size.cx - 5 - tR * 2, comboBox.size.cy / 2 - tR);
  paint.fillPath(comboBox.textColor);
  paint.closePath();
}

/*
 * 点击下拉菜单
 * comboBox:下拉菜单
 */
void clickComboBox(FCComboBox comboBox) {
  double showX = clientX(comboBox);
  double showY = clientY(comboBox) + comboBox.size.cy;
  comboBox.getDropDownMenu().location = FCPoint(showX, showY);
  comboBox.getDropDownMenu().visible = true;
  adjustMenu(comboBox.getDropDownMenu());
  invalidate(comboBox.getPaint());
}

/*
* 计算直线参数
* mp:坐标
* x1:横坐标1
* y1:纵坐标1
* x2:横坐标2
* y2:纵坐标2
* oX:坐标起始X
* oY:坐标起始Y
*/
void lineXY(FCChart chart, double x1, double y1, double x2, double y2,
    double oX, double oY) {
  chart.kChart = 0;
  chart.bChart = 0;
  if ((x1 - oX) != (x2 - oX)) {
    chart.kChart = ((y2 - oY) - (y1 - oY)) / ((x2 - oX) - (x1 - oX));
    chart.bChart = (y1 - oY) - chart.kChart * (x1 - oX);
  }
}

/*
* 判断是否选中直线
* mp:坐标
* x1:横坐标1
* y1:纵坐标1
* x2:横坐标2
* y2:纵坐标2
*/
bool selectLine(
    FCChart chart, FCPoint mp, double x1, double y1, double x2, double y2) {
  lineXY(chart, x1, y1, x2, y2, 0, 0);
  if (!(chart.kChart == 0 && chart.bChart == 0)) {
    if (mp.y / (mp.x * chart.kChart + chart.bChart) >= 0.9 &&
        mp.y / (mp.x * chart.kChart + chart.bChart) <= 1.1) {
      return true;
    }
  } else {
    if (mp.x >= x1 - chart.plotPointSizeChart &&
        mp.x <= x1 + chart.plotPointSizeChart) {
      return true;
    }
  }
  return false;
}

/*
* 判断是否选中射线
* mp:坐标
* x1:横坐标1
* y1:纵坐标1
* x2:横坐标2
* y2:纵坐标2
*/
bool selectRay(
    FCChart chart, FCPoint mp, double x1, double y1, double x2, double y2) {
  lineXY(chart, x1, y1, x2, y2, 0, 0);
  if (!(chart.kChart == 0 && chart.bChart == 0)) {
    if (mp.y / (mp.x * chart.kChart + chart.bChart) >= 0.9 &&
        mp.y / (mp.x * chart.kChart + chart.bChart) <= 1.1) {
      if (x1 >= x2) {
        if (mp.x > x1 + chart.plotPointSizeChart) {
          return false;
        }
      } else if (x1 < x2) {
        if (mp.x < x1 - chart.plotPointSizeChart) {
          return false;
        }
      }
      return true;
    }
  } else {
    if (mp.x >= x1 - chart.plotPointSizeChart &&
        mp.x <= x1 + chart.plotPointSizeChart) {
      if (y1 >= y2) {
        if (mp.y <= y1 - chart.plotPointSizeChart) {
          return true;
        }
      } else {
        if (mp.y >= y1 - chart.plotPointSizeChart) {
          return true;
        }
      }
    }
  }
  return false;
}

/*
* 判断是否选中线段
* mp:坐标
* x1:横坐标1
* y1:纵坐标1
* x2:横坐标2
* y2:纵坐标2
*/
bool selectSegment(
    FCChart chart, FCPoint mp, double x1, double y1, double x2, double y2) {
  lineXY(chart, x1, y1, x2, y2, 0, 0);
  double smallX = x1 <= x2 ? x1 : x2;
  double smallY = y1 <= y2 ? y1 : y2;
  double bigX = x1 > x2 ? x1 : x2;
  double bigY = y1 > y2 ? y1 : y2;
  if (mp.x >= smallX - 2 &&
      mp.x <= bigX + 2 &&
      mp.y >= smallY - 2 &&
      mp.y <= bigY + 2) {
    if (chart.kChart != 0 || chart.bChart != 0) {
      if (mp.y / (mp.x * chart.kChart + chart.bChart) >= 0.9 &&
          mp.y / (mp.x * chart.kChart + chart.bChart) <= 1.1) {
        return true;
      }
    } else {
      if (mp.x >= x1 - chart.plotPointSizeChart &&
          mp.x <= x1 + chart.plotPointSizeChart) {
        return true;
      }
    }
  }
  return false;
}

/*
* 根据三点计算圆心
* x1:横坐标1
* y1:纵坐标1
* x2:横坐标2
* y2:纵坐标2
* x3:横坐标3
* y3:纵坐标3
*/
void ellipseOR(FCChart chart, double x1, double y1, double x2, double y2,
    double x3, double y3) {
  chart.oXChart = ((y3 - y1) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1) +
          (y2 - y1) * (y1 * y1 - y3 * y3 + x1 * x1 - x3 * x3)) /
      (2 * (x2 - x1) * (y3 - y1) - 2 * (x3 - x1) * (y2 - y1));
  chart.oYChart = ((x3 - x1) * (x2 * x2 - x1 * x1 + y2 * y2 - y1 * y1) +
          (x2 - x1) * (x1 * x1 - x3 * x3 + y1 * y1 - y3 * y3)) /
      (2 * (y2 - y1) * (x3 - x1) - 2 * (y3 - y1) * (x2 - x1));
  chart.rChart = sqrt((x1 - chart.oXChart) * (x1 - chart.oXChart) +
      (y1 - chart.oYChart) * (y1 - chart.oYChart));
}

/*
* 判断点是否在椭圆上
* x:横坐标
* y:纵坐标
* oX:坐标起始X
* oY:坐标起始Y
* a:椭圆参数a
* b:椭圆参数b
*/
bool ellipseHasPoint(
    double x, double y, double oX, double oY, double a, double b) {
  x -= oX;
  y -= oY;
  if (a == 0 && b == 0 && x == 0 && y == 0) {
    return true;
  }
  if (a == 0) {
    if (x == 0 && y >= -b && y <= b) {
      return false;
    }
  }
  if (b == 0) {
    if (y == 0 && x >= -a && x <= a) {
      return true;
    }
  }
  if ((x * x) / (a * a) + (y * y) / (b * b) >= 0.8 &&
      (x * x) / (a * a) + (y * y) / (b * b) <= 1.2) {
    return true;
  }
  return false;
}

/*
* 计算线性回归
* list:集合
*/
double linearRegressionEquation(FCChart chart, List<double> list) {
  double result = 0;
  double sumX = 0;
  double sumY = 0;
  double sumUp = 0;
  double sumDown = 0;
  double xAvg = 0;
  double yAvg = 0;
  chart.kChart = 0;
  chart.bChart = 0;
  int length = list.length;
  if (length > 1) {
    for (int i = 0; i < length; i++) {
      sumX += i + 1;
      sumY += list[i];
    }
    xAvg = sumX / length;
    yAvg = sumY / length;
    for (int i = 0; i < length; i++) {
      sumUp += (i + 1 - xAvg) * (list[i] - yAvg);
      sumDown += (i + 1 - xAvg) * (i + 1 - xAvg);
    }
    chart.kChart = sumUp / sumDown;
    chart.bChart = yAvg - chart.kChart * xAvg;
  }
  return result;
}

/*
* 计算最大值
* list:集合
*/
double maxValue(List<double> list) {
  int length = list.length;
  double max = 0;
  for (int i = 0; i < length; i++) {
    if (i == 0) {
      max = list[i];
    } else {
      if (max < list[i]) {
        max = list[i];
      }
    }
  }
  return max;
}

/*
* 计算最小值
* list:集合
*/
double minValue(List<double> list) {
  int length = list.length;
  double min = 0;
  for (int i = 0; i < length; i++) {
    if (i == 0) {
      min = list[i];
    } else {
      if (min > list[i]) {
        min = list[i];
      }
    }
  }
  return min;
}

/*
* 计算平均值
* list:集合
*/
double avgValue(List<double> list) {
  double sum = 0;
  int length = list.length;
  if (length > 0) {
    for (int i = 0; i < length; i++) {
      sum += list[i];
    }
    return sum / length;
  }
  return 0;
}

/*
* 计算平行四边形参数
* x1:横坐标1
* y1:纵坐标1
* x2:横坐标2
* y2:纵坐标2
* x3:横坐标3
* y3:纵坐标3
*/
void parallelogram(FCChart chart, double x1, double y1, double x2, double y2,
    double x3, double y3) {
  chart.x4Chart = x1 + x3 - x2;
  chart.y4Chart = y1 + y3 - y2;
}

/*
* 计算斐波那契数列
* index:索引
*/
int fibonacciValue(int index) {
  if (index < 1) {
    return 0;
  } else {
    List<int> vList = <int>[];
    for (int i = 0; i < index; i++) {
      vList.add(0);
    }
    int result = 0;
    for (int i = 0; i <= index - 1; i++) {
      if (i == 0 || i == 1) {
        vList[i] = 1;
      } else {
        vList[i] = vList[i - 1] + vList[i - 2];
      }
    }
    result = vList[index - 1];
    return result;
  }
}

/*
* 获取图表的区域
* chart: 图表
* plot: 画线
*/
void getCandleRange(FCChart chart, FCPlot plot) {
  int bIndex = getChartIndexByDate(chart, plot.key1);
  int eIndex = getChartIndexByDate(chart, plot.key2);
  int tempBIndex = min(bIndex, eIndex);
  int tempEIndex = max(bIndex, eIndex);
  bIndex = tempBIndex;
  eIndex = tempEIndex;
  List<double> highList = <double>[];
  List<double> lowList = <double>[];
  for (int i = bIndex; i <= eIndex; i++) {
    highList.add(chart.data[i].high);
    lowList.add(chart.data[i].low);
  }
  chart.nHighChart = maxValue(highList);
  chart.nLowChart = minValue(lowList);
}

/*
* 获取百分比线的刻度
* y1: 纵坐标1
* y2: 纵坐标2
*/
List<double> getPercentParams(double y1, double y2) {
  double y0 = 0, y25 = 0, y50 = 0, y75 = 0, y100 = 0;
  y0 = y1;
  y25 = y1 <= y2 ? y1 + (y2 - y1) / 4 : y2 + (y1 - y2) * 3 / 4;
  y50 = y1 <= y2 ? y1 + (y2 - y1) / 2 : y2 + (y1 - y2) / 2;
  y75 = y1 <= y2 ? y1 + (y2 - y1) * 3 / 4 : y2 + (y1 - y2) / 4;
  y100 = y2;
  List<double> list = <double>[];
  list.add(y0);
  list.add(y25);
  list.add(y50);
  list.add(y75);
  list.add(y100);
  return list;
}

/*
* 根据坐标计算矩形
* x1:横坐标1
* y1:纵坐标1
* x2:横坐标2
* y2:纵坐标2
*/
void rectangleXYWH(FCChart chart, double x1, double y1, double x2, double y2) {
  chart.xChart = x1 < x2 ? x1 : x2;
  chart.yChart = y1 < y2 ? y1 : y2;
  chart.wChart = x1 - x2 > 0 ? x1 - x2 : x2 - x1;
  chart.hChart = y1 - y2 > 0 ? y1 - y2 : y2 - y1;
  if (chart.wChart <= 0) {
    chart.wChart = 4;
  }
  if (chart.hChart <= 0) {
    chart.hChart = 4;
  }
}

/*
 * 计算最大值
 * @param {Object} ticks 最高价数组
 * @param {Object} days
 */
List<double> hhvValue(List<double> ticks, int days) {
  List<double> hhv = <double>[];
  double max = ticks[0];
  for (int i = 0; i < ticks.length; i++) {
    if (i >= days) {
      max = ticks[i];
      for (int j = i; j > i - days; j--) {
        if (max < ticks[j]) {
          max = ticks[j];
        }
      }
      hhv.add(max);
    } else {
      if (max < ticks[i]) {
        max = ticks[i];
      }
      hhv.add(max);
    }
  }
  return hhv;
}

/*
 * 计算最小值
 * @param {Object} ticks 最低价数组
 * @param {Object} days
 */
List<double> llvValue(List<double> ticks, int days) {
  List<double> llv = <double>[];
  double min = ticks[0];
  for (int i = 0; i < ticks.length; i++) {
    if (i >= days) {
      min = ticks[i];
      for (int j = i; j > i - days; j--) {
        if (min > ticks[j]) {
          min = ticks[j];
        }
      }
      llv.add(min);
    } else {
      if (min > ticks[i]) {
        min = ticks[i];
      }
      llv.add(min);
    }
  }
  return llv;
}

/*
* REF函数
* ticks:数据
* days:日数
*/
List<double> refValue(List<double> ticks, int days) {
  List<double> refArr = <double>[];
  int length = ticks.length;
  for (int i = 0; i < length; i++) {
    double ref = 0;
    if (i >= days) {
      ref = ticks[i - days];
    } else {
      ref = ticks[0];
    }
    refArr.add(ref);
  }
  return refArr;
}

/*
* 获取TRIX的数据
* ticks:数据
*/
void getTRIXData(
    List<double> ticks, int m, List<double> trixArr, List<double> matrixArr) {
  int length = ticks.length;
  List<double> mtrArr = <double>[];
  List<double> emaArr1 = <double>[];
  emaArr1.add(ticks[0]);
  for (int i = 1; i < length; i++) {
    emaArr1.add(getEMA(m, ticks[i], emaArr1[i - 1]));
  }

  List<double> emaArr2 = <double>[];
  emaArr2.add(emaArr1[0]);
  for (int i = 1; i < length; i++) {
    emaArr2.add(getEMA(m, emaArr1[i], emaArr2[i - 1]));
  }

  mtrArr.add(emaArr2[0]);
  for (int i = 1; i < length; i++) {
    mtrArr.add(getEMA(m, emaArr2[i], mtrArr[i - 1]));
  }

  List<double> ref = refValue(mtrArr, 1);
  for (int i = 0; i < length; i++) {
    double trix = 100 * (mtrArr[i] - ref[i]) / ref[i];
    trixArr.add(trix);
  }

  List<double> matrixArrCopy = maValue(trixArr, m);
  for (int i = 0; i < matrixArrCopy.length; i++) {
    matrixArr.add(matrixArrCopy[i]);
  }
}

/*
* 获取方差数据
*/
double standardDeviationSum(List<double> listValue, double avgValue) {
  double targetValue = listValue[listValue.length - 1];
  double sumValue = (targetValue - avgValue) * (targetValue - avgValue);
  for (int i = 0; i < listValue.length - 1; i++) {
    double ileft = listValue[i];
    sumValue = sumValue + (ileft - avgValue) * (ileft - avgValue);
  }
  return sumValue;
}

/*
 * 计算boll指标,ma的周期为20日
 *
 * @method BOLL
 * @param {Array} ticks
 * 一维数组类型，每个元素为当前Tick的收盘价格
 * @return {Object} 返回一个包含upper mid lower属性的对象,每个属性对应的类型为{Array[Number]}
 */
void getBollData(List<double> ticks, int maDays, List<double> ups,
    List<double> mas, List<double> lows) {
  int tickBegin = maDays - 1;
  double maSum = 0;
  double p = 0;
  for (int i = 0; i < ticks.length; i++) {
    double c = ticks[i];
    double ma = 0;
    double md = 0;
    int bstart = 0;
    double mdSum = 0;
    maSum = maSum + c;
    if (i >= tickBegin) {
      maSum = maSum - p;
      ma = maSum / maDays;
      bstart = i - tickBegin;
      p = ticks[bstart];
      mas.add(ma);
      bstart = i - tickBegin;
      p = ticks[bstart];
      List<double> values = <double>[];
      for (int j = bstart; j < bstart + maDays; j++) {
        values.add(ticks[j]);
      }
      mdSum = standardDeviationSum(values, ma);
      md = sqrt(mdSum / maDays);
      ups.add(ma + 2 * md);
      lows.add(ma - 2 * md);
    } else {
      ma = maSum / (i + 1);
      mas.add(ma);
      List<double> values = <double>[];
      for (int j = 0; j < i + 1; j++) {
        values.add(ticks[j]);
      }
      mdSum = standardDeviationSum(values, ma);
      md = sqrt(mdSum / (i + 1));
      ups.add(ma + 2 * md);
      lows.add(ma - 2 * md);
    }
  }
}

/*
 *
 * 计算kdj指标,rsv的周期为9日
 *
 * @method KDJ
 * @param {Array} ticks
 * 二维数组类型，其中内层数组包含三个元素值，第一个值表示当前Tick的最高价格，第二个表示当前Tick的最低价格，第三个表示当前Tick的收盘价格
 * @return {Object} 返回一个包含k d j属性的对象,每个属性对应的类型为{Array[Number]}
 */
void getKDJData(
    List<double> highArr,
    List<double> lowArr,
    List<double> closeArr,
    int n,
    int m1,
    int m2,
    List<double> ks,
    List<double> ds,
    List<double> js) {
  List<double> rsvs = <double>[];
  double lastK = 0;
  double lastD = 0;
  double curK = 0;
  double curD = 0;
  for (int i = 0; i < closeArr.length; i++) {
    List<double> highList = <double>[];
    List<double> lowList = <double>[];
    int startIndex = i - n;
    if (startIndex < 0) {
      startIndex = 0;
    }

    for (int j = startIndex; j <= i; j++) {
      highList.add(highArr[j]);
      lowList.add(lowArr[j]);
    }

    double close = closeArr[i];
    double maxHigh = 0, minLow = 0;
    for (int m = 0; m < highList.length; m++) {
      double high = highList[m];
      double low = lowList[m];
      if (high > maxHigh) {
        maxHigh = high;
      }
      if (low < minLow) {
        minLow = low;
      }
    }
    if (maxHigh == minLow) {
      rsvs.add(0);
    } else {
      rsvs.add((close - minLow) / (maxHigh - minLow) * 100);
    }
    if (i == 0) {
      lastK = rsvs[i];
      lastD = rsvs[i];
    }
    curK = (m1 - 1) / m1 * lastK + 1.0 / m1 * rsvs[i];
    ks.add(curK);
    lastK = curK;

    curD = (m2 - 1) / m2 * lastD + 1.0 / m2 * curK;
    ds.add(curD);
    lastD = curD;

    js.add(3.0 * curK - 2.0 * curD);
  }
}

/*
 *
 * 计算rsi指标,分别返回以6日，12日，24日为参考基期的RSI值
 *
 * @method RSI
 * @param {Array} ticks
 * 一维数组类型，每个元素为当前Tick的收盘价格
 * @return {Object} 返回一个包含rsi6 rsi12 rsi24属性的对象,每个属性对应的类型为{Array[Number]}
 */
void getRSIData(List<double> ticks, int n1, int n2, int n3, List<double> rsi1,
    List<double> rsi2, List<double> rsi3) {
  double lastClosePx = ticks[0];
  double lastSm1 = 0;
  double lastSa1 = 0;
  double lastSm2 = 0;
  double lastSa2 = 0;
  double lastSm3 = 0;
  double lastSa3 = 0;
  for (int i = 0; i < ticks.length; i++) {
    double c = ticks[i];
    double m = max(c - lastClosePx, 0);
    double a = (c - lastClosePx).abs();
    if (i == 0) {
      lastSm1 = 0;
      lastSa1 = 0;
      rsi1.add(0);
    } else {
      lastSm1 = (m + (n1 - 1) * lastSm1) / n1;
      lastSa1 = (a + (n1 - 1) * lastSa1) / n1;
      if (lastSa1 != 0) {
        rsi1.add(lastSm1 / lastSa1 * 100);
      } else {
        rsi1.add(0);
      }
    }
    if (i == 0) {
      lastSm2 = 0;
      lastSa2 = 0;
      rsi2.add(0);
    } else {
      lastSm2 = (m + (n2 - 1) * lastSm2) / n2;
      lastSa2 = (a + (n2 - 1) * lastSa2) / n2;
      if (lastSa2 != 0) {
        rsi2.add(lastSm2 / lastSa2 * 100);
      } else {
        rsi2.add(0);
      }
    }
    if (i == 0) {
      lastSm3 = 0;
      lastSa3 = 0;
      rsi3.add(0);
    } else {
      lastSm3 = (m + (n3 - 1) * lastSm3) / n3;
      lastSa3 = (a + (n3 - 1) * lastSa3) / n3;
      if (lastSa3 != 0) {
        rsi3.add(lastSm3 / lastSa3 * 100);
      } else {
        rsi3.add(0.0);
      }
    }
    lastClosePx = c;
  }
}

/*
 * MA数据计算
 * @param {Object} ticks 收盘价数组
 * @param {Object} days 天数
 */
List<double> maValue(List<double> ticks, int days) {
  double maSum = 0;
  List<double> mas = <double>[];
  double last = 0;
  for (int i = 0; i < ticks.length; i++) {
    double ma = 0;
    if (i >= days) {
      last = ticks[i - days];
      maSum = maSum + ticks[i] - last;
      ma = maSum / days;
    } else {
      maSum += ticks[i];
      ma = maSum / (i + 1);
    }
    mas.add(ma);
  }
  return mas;
}

/*
 * 计算ROC数据
 * @param {Object} ticks 收盘价数组
 */
void getRocData(
    List<double> ticks, int n, int m, List<double> roc, List<double> maroc) {
  for (int i = 0; i < ticks.length; i++) {
    double currRoc = 0;
    if (i >= n) {
      currRoc = 100 * (ticks[i] - ticks[i - n]) / ticks[i - n];
      roc.add(currRoc);
    } else {
      currRoc = 100 * (ticks[i] - ticks[0]) / ticks[0];
      roc.add(currRoc);
    }
  }
  List<double> marocMA = maValue(roc, m);
  for (int i = 0; i < marocMA.length; i++) {
    maroc.add(marocMA[i]);
  }
}

/*
 * 获取BIAS的数据
 * @param {Array} ticks 收盘价数组
 * @return 
 */
void getBIASData(List<double> ticks, int n1, int n2, int n3,
    List<double> bias1Arr, List<double> bias2Arr, List<double> bias3Arr) {
  List<double> ma1 = maValue(ticks, n1);
  List<double> ma2 = maValue(ticks, n2);
  List<double> ma3 = maValue(ticks, n3);
  for (int i = 0; i < ticks.length; i++) {
    double b1 = (ticks[i] - ma1[i]) / ma1[i] * 100;
    double b2 = (ticks[i] - ma2[i]) / ma2[i] * 100;
    double b3 = (ticks[i] - ma3[i]) / ma3[i] * 100;
    bias1Arr.add(b1);
    bias2Arr.add(b2);
    bias3Arr.add(b3);
  }
}

/*
 * 计算DMA（平均差）
 * @param {Object} ticks 收盘价数组
 */
void getDMAData(List<double> ticks, int n1, int n2, List<double> difArr,
    List<double> difmaArr) {
  List<double> ma10 = maValue(ticks, n1);
  List<double> ma50 = maValue(ticks, n2);
  for (int i = 0; i < ticks.length; i++) {
    double dif = ma10[i] - ma50[i];
    difArr.add(dif);
  }
  List<double> difma = maValue(difArr, n1);
  for (int i = 0; i < difma.length; i++) {
    difmaArr.add(difma[i]);
  }
}

/*
 * 计算BBI(多空指标)
 * @param {Object} ticks
 */
void getBBIData(
    List<double> ticks, int n1, int n2, int n3, int n4, List<double> bbiArr) {
  List<double> ma3 = maValue(ticks, n1);
  List<double> ma6 = maValue(ticks, n2);
  List<double> ma12 = maValue(ticks, n3);
  List<double> ma24 = maValue(ticks, n4);
  for (int i = 0; i < ticks.length; i++) {
    double bbi = (ma3[i] + ma6[i] + ma12[i] + ma24[i]) / 4;
    bbiArr.add(bbi);
  }
}

/*
 * 计算WR(威廉指标)
 * @param {Array} ticks 含最高价,最低价, 收盘价的二维数组
 * @param {Object} days
 */
void getWRData(List<double> highArr, List<double> lowArr, List<double> closeArr,
    int n1, int n2, List<double> wr1Arr, List<double> wr2Arr) {
  List<double> highArr1 = hhvValue(highArr, n1);
  List<double> highArr2 = hhvValue(highArr, n2);
  List<double> lowArr1 = llvValue(lowArr, n1);
  List<double> lowArr2 = llvValue(lowArr, n2);
  for (int i = 0; i < closeArr.length; i++) {
    double high1 = highArr1[i];
    double low1 = lowArr1[i];
    double high2 = highArr2[i];
    double low2 = lowArr2[i];
    double close = closeArr[i];
    double wr1 = 100 * (high1 - close) / (high1 - low1);
    double wr2 = 100 * (high2 - close) / (high2 - low2);
    wr1Arr.add(wr1);
    wr2Arr.add(wr2);
  }
}

/*
 * CCI(顺势指标)计算  CCI（N日）=（TP－MA）÷MD÷0.015
 * @param {Object} ticks 带最高价，最低价，收盘价的二维数组
 */
void getCCIData(List<double> highArr, List<double> lowArr,
    List<double> closeArr, int n, List<double> cciArr) {
  List<double> tpArr = <double>[];
  for (int i = 0; i < closeArr.length; i++) {
    tpArr.add((closeArr[i] + highArr[i] + lowArr[i]) / 3);
  }
  List<double> maClose = maValue(closeArr, n);

  List<double> mdArr = <double>[];
  for (int i = 0; i < closeArr.length; i++) {
    mdArr.add(maClose[i] - closeArr[i]);
  }
  List<double> maMD = maValue(mdArr, n);
  for (int i = 0; i < closeArr.length; i++) {
    double cci = 0;
    if (maMD[i] > 0) {
      cci = (tpArr[i] - maClose[i]) / (maMD[i] * 0.015);
    }
    cciArr.add(cci);
  }
}

/*
* 选中直线
* chart: 图表
* mp:坐标
*/
FCPlot selectPlot(FCChart chart, FCPoint mp) {
  FCPlot sPlot = FCPlot();
  sPlot.isNotEmpty = false;
  chart.startMovePlot = false;
  chart.selectPlotPoint = -1;
  for (int i = 0; i < chart.plots.length; i++) {
    FCPlot plot = chart.plots[i];
    int index1 = 0, index2 = 0, index3 = 0;
    double mpx1 = 0, mpy1 = 0, mpx2 = 0, mpy2 = 0, mpx3 = 0, mpy3 = 0;
    //检查关键点
    if (plot.key1 > 0) {
      index1 = getChartIndexByDate(chart, plot.key1);
      mpx1 = getChartX(chart, index1);
      mpy1 = getChartY(chart, 0, plot.value1);
      if (mp.x >= mpx1 - chart.plotPointSizeChart &&
          mp.x <= mpx1 + chart.plotPointSizeChart &&
          mp.y >= mpy1 - chart.plotPointSizeChart &&
          mp.y <= mpy1 + chart.plotPointSizeChart) {
        sPlot = plot;
        chart.selectPlotPoint = 0;
        break;
      }
    }
    if (plot.key2 > 0) {
      index2 = getChartIndexByDate(chart, plot.key2);
      mpx2 = getChartX(chart, index2);
      mpy2 = getChartY(chart, 0, plot.value2);
      if (mp.x >= mpx2 - chart.plotPointSizeChart &&
          mp.x <= mpx2 + chart.plotPointSizeChart &&
          mp.y >= mpy2 - chart.plotPointSizeChart &&
          mp.y <= mpy2 + chart.plotPointSizeChart) {
        sPlot = plot;
        chart.selectPlotPoint = 1;
        break;
      }
    }
    if (plot.key3 > 0) {
      index3 = getChartIndexByDate(chart, plot.key3);
      mpx3 = getChartX(chart, index3);
      mpy3 = getChartY(chart, 0, plot.value3);
      if (mp.x >= mpx3 - chart.plotPointSizeChart &&
          mp.x <= mpx3 + chart.plotPointSizeChart &&
          mp.y >= mpy3 - chart.plotPointSizeChart &&
          mp.y <= mpy3 + chart.plotPointSizeChart) {
        sPlot = plot;
        chart.selectPlotPoint = 2;
        break;
      }
    }
    //判断其余部分的选中
    if (chart.selectPlotPoint == -1) {
      if (plot.plotType == "Line") {
        chart.startMovePlot = selectLine(chart, mp, mpx1, mpy1, mpx2, mpy2);
      } else if (plot.plotType == "ArrowSegment") {
        chart.startMovePlot = selectSegment(chart, mp, mpx1, mpy1, mpx2, mpy2);
      } else if (plot.plotType == "AngleLine") {
        chart.startMovePlot = selectLine(chart, mp, mpx1, mpy1, mpx2, mpy2);
        if (!chart.startMovePlot) {
          chart.startMovePlot = selectLine(chart, mp, mpx1, mpy1, mpx3, mpy3);
        }
      } else if (plot.plotType == "Parallel") {
        chart.startMovePlot = selectLine(chart, mp, mpx1, mpy1, mpx2, mpy2);
        if (!chart.startMovePlot) {
          lineXY(chart, mpx1, mpy1, mpx2, mpy2, 0, 0);
          double newB = mpy3 - chart.kChart * mpx3;
          if (mpx2 == mpx1) {
            if (mp.x >= mpx3 - chart.plotPointSizeChart &&
                mp.x <= mpx3 + chart.plotPointSizeChart) {
              chart.startMovePlot = true;
            }
          } else {
            double newX1 = chart.leftVScaleWidth;
            double newY1 = newX1 * chart.kChart + newB;
            double newX2 = chart.size.cx - chart.rightVScaleWidth;
            double newY2 = newX2 * chart.kChart + newB;
            chart.startMovePlot =
                selectLine(chart, mp, newX1, newY1, newX2, newY2);
          }
        }
      } else if (plot.plotType == "LRLine") {
        chart.startMovePlot = selectSegment(chart, mp, mpx1, mpy1, mpx2, mpy2);
      } else if (plot.plotType == "Segment") {
        chart.startMovePlot = selectSegment(chart, mp, mpx1, mpy1, mpx2, mpy2);
      } else if (plot.plotType == "Ray") {
        chart.startMovePlot = selectRay(chart, mp, mpx1, mpy1, mpx2, mpy2);
      } else if (plot.plotType == "Triangle") {
        chart.startMovePlot = selectSegment(chart, mp, mpx1, mpy1, mpx2, mpy2);
        if (!chart.startMovePlot) {
          chart.startMovePlot =
              selectSegment(chart, mp, mpx2, mpy2, mpx3, mpy3);
        }
        if (!chart.startMovePlot) {
          chart.startMovePlot =
              selectSegment(chart, mp, mpx1, mpy1, mpx3, mpy3);
        }
      } else if (plot.plotType == "SymmetricTriangle") {
        if (mpx2 != mpx1) {
          double a = (mpy2 - mpy1) / (mpx2 - mpx1);
          double b = mpy1 - a * mpx1;
          double c = -a;
          double d = mpy3 - c * mpx3;
          double leftX = chart.leftVScaleWidth;
          double leftY = leftX * a + b;
          double rightX = chart.size.cx - chart.rightVScaleWidth;
          double rightY = rightX * a + b;
          chart.startMovePlot =
              selectSegment(chart, mp, leftX, leftY, rightX, rightY);
          if (!chart.startMovePlot) {
            leftY = leftX * c + d;
            rightY = rightX * c + d;
            chart.startMovePlot =
                selectSegment(chart, mp, leftX, leftY, rightX, rightY);
          }
        } else {
          double divHeight = getCandleDivHeight(chart);
          chart.startMovePlot =
              selectSegment(chart, mp, mpx1, 0, mpx1, divHeight);
          if (!chart.startMovePlot) {
            chart.startMovePlot =
                selectSegment(chart, mp, mpx3, 0, mpx3, divHeight);
          }
        }
      } else if (plot.plotType == "Rect") {
        double sX1 = min(mpx1, mpx2);
        double sY1 = min(mpy1, mpy2);
        double sX2 = max(mpx1, mpx2);
        double sY2 = max(mpy1, mpy2);
        chart.startMovePlot = selectSegment(chart, mp, sX1, sY1, sX2, sY1);
        if (!chart.startMovePlot) {
          chart.startMovePlot = selectSegment(chart, mp, sX2, sY1, sX2, sY2);
        }
        if (!chart.startMovePlot) {
          chart.startMovePlot = selectSegment(chart, mp, sX1, sY2, sX2, sY2);
        }
        if (!chart.startMovePlot) {
          chart.startMovePlot = selectSegment(chart, mp, sX1, sY1, sX1, sY2);
        }
      } else if (plot.plotType == "BoxLine") {
        double sX1 = min(mpx1, mpx2);
        double sY1 = min(mpy1, mpy2);
        double sX2 = max(mpx1, mpx2);
        double sY2 = max(mpy1, mpy2);
        chart.startMovePlot = selectSegment(chart, mp, sX1, sY1, sX2, sY1);
        if (!chart.startMovePlot) {
          chart.startMovePlot = selectSegment(chart, mp, sX2, sY1, sX2, sY2);
        }
        if (!chart.startMovePlot) {
          chart.startMovePlot = selectSegment(chart, mp, sX1, sY2, sX2, sY2);
        }
        if (!chart.startMovePlot) {
          chart.startMovePlot = selectSegment(chart, mp, sX1, sY1, sX1, sY2);
        }
      } else if (plot.plotType == "TironeLevels") {
        double sX1 = min(mpx1, mpx2);
        double sY1 = min(mpy1, mpy2);
        double sX2 = max(mpx1, mpx2);
        double sY2 = max(mpy1, mpy2);
        chart.startMovePlot = selectSegment(chart, mp, sX1, sY1, sX2, sY1);
        if (!chart.startMovePlot) {
          chart.startMovePlot = selectSegment(chart, mp, sX1, sY2, sX2, sY2);
        }
      } else if (plot.plotType == "QuadrantLines") {
        double sX1 = min(mpx1, mpx2);
        double sY1 = min(mpy1, mpy2);
        double sX2 = max(mpx1, mpx2);
        double sY2 = max(mpy1, mpy2);
        chart.startMovePlot = selectSegment(chart, mp, sX1, sY1, sX2, sY1);
        if (!chart.startMovePlot) {
          chart.startMovePlot = selectSegment(chart, mp, sX1, sY2, sX2, sY2);
        }
      } else if (plot.plotType == "GoldenRatio") {
        double sY1 = min(mpy1, mpy2);
        double sY2 = max(mpy1, mpy2);
        List<double> ranges = <double>[];
        ranges.add(0);
        ranges.add(0.236);
        ranges.add(0.382);
        ranges.add(0.5);
        ranges.add(0.618);
        ranges.add(0.809);
        ranges.add(1);
        ranges.add(1.382);
        ranges.add(1.618);
        ranges.add(2);
        ranges.add(2.382);
        ranges.add(2.618);
        for (int j = 0; j < ranges.length; j++) {
          double newY = sY1 <= sY2
              ? sY1 + (sY2 - sY1) * ranges[j]
              : sY2 + (sY1 - sY2) * (1 - ranges[j]);
          chart.startMovePlot = selectSegment(chart, mp, chart.leftVScaleWidth,
              newY, chart.size.cx - chart.rightVScaleWidth, newY);
          if (chart.startMovePlot) {
            break;
          }
        }
      } else if (plot.plotType == "Cycle") {
        double r = sqrt(
            ((mpx2 - mpx1) * (mpx2 - mpx1) + (mpy2 - mpy1) * (mpy2 - mpy1))
                .abs());
        double round =
            (mp.x - mpx1) * (mp.x - mpx1) + (mp.y - mpy1) * (mp.y - mpy1);
        if (round / (r * r) >= 0.9 && round / (r * r) <= 1.1) {
          chart.startMovePlot = true;
        }
      } else if (plot.plotType == "CircumCycle") {
        ellipseOR(chart, mpx1, mpy1, mpx2, mpy2, mpx3, mpy3);
        double round = (mp.x - chart.oXChart) * (mp.x - chart.oXChart) +
            (mp.y - chart.oYChart) * (mp.y - chart.oYChart);
        if (round / (chart.rChart * chart.rChart) >= 0.9 &&
            round / (chart.rChart * chart.rChart) <= 1.1) {
          chart.startMovePlot = true;
        }
      } else if (plot.plotType == "Ellipse") {
        double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
        if (mpx1 <= mpx2) {
          x1 = mpx2;
          y1 = mpy2;
          x2 = mpx1;
          y2 = mpy1;
        } else {
          x1 = mpx1;
          y1 = mpy1;
          x2 = mpx2;
          y2 = mpy2;
        }
        double x = x1 - (x1 - x2);
        double y = 0;
        double width = (x1 - x2) * 2;
        double height = 0;
        if (y1 >= y2) {
          height = (y1 - y2) * 2;
        } else {
          height = (y2 - y1) * 2;
        }
        y = y2 - height / 2;
        double a = width / 2;
        double b = height / 2;
        chart.startMovePlot = ellipseHasPoint(
            mp.x, mp.y, x + (width / 2), y + (height / 2), a, b);
      } else if (plot.plotType == "LRBand") {
        chart.startMovePlot = selectSegment(chart, mp, mpx1, mpy1, mpx2, mpy2);
        if (!chart.startMovePlot) {
          List<double> list = <double>[];
          int minIndex = min(index1, index2);
          int maxIndex = max(index1, index2);
          for (int j = minIndex; j <= maxIndex; j++) {
            list.add(chart.data[j].close);
          }
          linearRegressionEquation(chart, list);
          getLRBandRange(chart, plot, chart.kChart, chart.bChart);
          mpy1 = getChartY(chart, 0, plot.value1 + chart.upSubValue);
          mpy2 = getChartY(chart, 0, plot.value2 + chart.upSubValue);
          chart.startMovePlot =
              selectSegment(chart, mp, mpx1, mpy1, mpx2, mpy2);
          if (!chart.startMovePlot) {
            mpy1 = getChartY(chart, 0, plot.value1 - chart.downSubValue);
            mpy2 = getChartY(chart, 0, plot.value2 - chart.downSubValue);
            chart.startMovePlot =
                selectSegment(chart, mp, mpx1, mpy1, mpx2, mpy2);
          }
        }
      } else if (plot.plotType == "LRChannel") {
        lineXY(chart, mpx1, mpy1, mpx2, mpy2, 0, 0);
        double rightX = chart.size.cx - chart.rightVScaleWidth;
        double rightY = rightX * chart.kChart + chart.bChart;
        chart.startMovePlot =
            selectSegment(chart, mp, mpx1, mpy1, rightX, rightY);
        if (!chart.startMovePlot) {
          List<double> list = <double>[];
          int minIndex = min(index1, index2);
          int maxIndex = max(index1, index2);
          for (int j = minIndex; j <= maxIndex; j++) {
            list.add(chart.data[j].close);
          }
          linearRegressionEquation(chart, list);
          getLRBandRange(chart, plot, chart.kChart, chart.bChart);
          mpy1 = getChartY(chart, 0, plot.value1 + chart.upSubValue);
          mpy2 = getChartY(chart, 0, plot.value2 + chart.upSubValue);
          lineXY(chart, mpx1, mpy1, mpx2, mpy2, 0, 0);
          rightY = rightX * chart.kChart + chart.bChart;
          chart.startMovePlot =
              selectSegment(chart, mp, mpx1, mpy1, rightX, rightY);
          if (!chart.startMovePlot) {
            mpy1 = getChartY(chart, 0, plot.value1 - chart.downSubValue);
            mpy2 = getChartY(chart, 0, plot.value2 - chart.downSubValue);
            lineXY(chart, mpx1, mpy1, mpx2, mpy2, 0, 0);
            rightY = rightX * chart.kChart + chart.bChart;
            chart.startMovePlot =
                selectSegment(chart, mp, mpx1, mpy1, rightX, rightY);
          }
        }
      } else if (plot.plotType == "ParalleGram") {
        parallelogram(chart, mpx1, mpy1, mpx2, mpy2, mpx3, mpy3);
        chart.startMovePlot = selectSegment(chart, mp, mpx1, mpy1, mpx2, mpy2);
        if (!chart.startMovePlot) {
          chart.startMovePlot =
              selectSegment(chart, mp, mpx2, mpy2, mpx3, mpy3);
          if (!chart.startMovePlot) {
            chart.startMovePlot = selectSegment(
                chart, mp, mpx3, mpy3, chart.x4Chart, chart.y4Chart);
            if (!chart.startMovePlot) {
              chart.startMovePlot = selectSegment(
                  chart, mp, chart.x4Chart, chart.y4Chart, mpx1, mpy1);
            }
          }
        }
      } else if (plot.plotType == "SpeedResist") {
        chart.startMovePlot = selectSegment(chart, mp, mpx1, mpy1, mpx2, mpy2);
        if (!chart.startMovePlot) {
          if (mpx1 != mpx2 && mpy1 != mpy2) {
            FCPoint firstP = FCPoint(mpx2, mpy2 - (mpy2 - mpy1) / 3);
            FCPoint secondP = FCPoint(mpx2, mpy2 - (mpy2 - mpy1) * 2 / 3);
            FCPoint startP = FCPoint(mpx1, mpy1);
            double fK = 0, fB = 0, sK = 0, sB = 0;
            lineXY(chart, startP.x, startP.y, firstP.x, firstP.y, 0, 0);
            fK = chart.kChart;
            fB = chart.bChart;
            lineXY(chart, startP.x, startP.y, secondP.x, secondP.y, 0, 0);
            sK = chart.kChart;
            sB = chart.bChart;
            double newYF = 0, newYS = 0;
            double newX = 0;
            if (mpx2 > mpx1) {
              newYF = fK * (chart.size.cx - chart.rightVScaleWidth) + fB;
              newYS = sK * (chart.size.cx - chart.rightVScaleWidth) + sB;
              newX = (chart.size.cx - chart.rightVScaleWidth);
            } else {
              newYF = fB;
              newYS = sB;
              newX = chart.leftVScaleWidth;
            }
            chart.startMovePlot =
                selectSegment(chart, mp, startP.x, startP.y, newX, newYF);
            if (!chart.startMovePlot) {
              chart.startMovePlot =
                  selectSegment(chart, mp, startP.x, startP.y, newX, newYS);
            }
          }
        }
      } else if (plot.plotType == "FiboFanline") {
        chart.startMovePlot = selectSegment(chart, mp, mpx1, mpy1, mpx2, mpy2);
        if (!chart.startMovePlot) {
          if (mpx1 != mpx2 && mpy1 != mpy2) {
            FCPoint firstP = FCPoint(mpx2, mpy2 - (mpy2 - mpy1) * 0.382);
            FCPoint secondP = FCPoint(mpx2, mpy2 - (mpy2 - mpy1) * 0.5);
            FCPoint thirdP = FCPoint(mpx2, mpy2 - (mpy2 - mpy1) * 0.618);
            FCPoint startP = FCPoint(mpx1, mpy1);
            List<FCPoint> listP = <FCPoint>[];
            listP.add(firstP);
            listP.add(secondP);
            listP.add(thirdP);
            int listSize = listP.length;
            for (int j = 0; j < listSize; j++) {
              lineXY(chart, startP.x, startP.y, listP[j].x, listP[j].y, 0, 0);
              double newX = 0;
              double newY = 0;
              if (mpx2 > mpx1) {
                newY = chart.kChart * (chart.size.cx - chart.rightVScaleWidth) +
                    chart.bChart;
                newX = (chart.size.cx - chart.rightVScaleWidth);
              } else {
                newY = chart.bChart;
                newX = chart.leftVScaleWidth;
              }
              chart.startMovePlot =
                  selectSegment(chart, mp, startP.x, startP.y, newX, newY);
              if (chart.startMovePlot) {
                break;
              }
            }
          }
        }
      } else if (plot.plotType == "FiboTimezone") {
        int fValue = 1;
        int aIndex = index1;
        int pos = 1;
        double divHeight = getCandleDivHeight(chart);
        chart.startMovePlot =
            selectSegment(chart, mp, mpx1, 0, mpx1, divHeight);
        if (!chart.startMovePlot) {
          while (aIndex + fValue <= chart.lastVisibleIndex) {
            fValue = fibonacciValue(pos);
            int newIndex = aIndex + fValue;
            double newX = getChartX(chart, newIndex);
            chart.startMovePlot =
                selectSegment(chart, mp, newX, 0, newX, divHeight);
            if (chart.startMovePlot) {
              break;
            }
            pos++;
          }
        }
      } else if (plot.plotType == "Percent") {
        List<double> list = getPercentParams(mpy1, mpy2);
        for (int j = 0; j < list.length; j++) {
          chart.startMovePlot = selectSegment(chart, mp, chart.leftVScaleWidth,
              list[j], chart.size.cx - chart.rightVScaleWidth, list[j]);
          if (chart.startMovePlot) {
            break;
          }
        }
      }
      if (chart.startMovePlot) {
        sPlot = plot;
        plot.startKey1 = plot.key1;
        plot.startValue1 = plot.value1;
        plot.startKey2 = plot.key2;
        plot.startValue2 = plot.value2;
        plot.startKey3 = plot.key3;
        plot.startValue3 = plot.value3;
        break;
      }
    }
  }
  return sPlot;
}

/*
* 添加画线
* chart: 图表
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
*/
void addPlotDefault(FCChart chart, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint) {
  FCPoint mp = firstPoint;
  if (mp.y < getCandleDivHeight(chart)) {
    int touchIndex = getChartIndex(chart, mp);
    if (touchIndex >= chart.firstVisibleIndex &&
        touchIndex <= chart.lastVisibleIndex) {
      if (chart.addingPlotChart == "FiboTimezone") {
        int fIndex = touchIndex;
        double fDate = getChartDateByIndex(chart, fIndex);
        double y = getChartValue(chart, mp);
        FCPlot newPlot = FCPlot();
        if (chart.getPaint().defaultUIStyle == "light") {
          newPlot.lineColor = "rgb(0,0,0)";
          newPlot.pointColor = "rgba(0,0,0,125)";
        } else {
          newPlot.lineColor = "rgb(255,255,255)";
          newPlot.pointColor = "rgba(255,255,255,125)";
        }
        newPlot.key1 = fDate;
        newPlot.value1 = y;
        newPlot.plotType = chart.addingPlotChart;
        chart.plots.add(newPlot);
        chart.sPlot = <FCPlot>[];
        FCPlot selectedPlot = selectPlot(chart, mp);
        if (selectedPlot.isNotEmpty) {
          chart.setSelectedPlot(selectedPlot);
        }
      } else if (chart.addingPlotChart == "Triangle" ||
          chart.addingPlotChart == "CircumCycle" ||
          chart.addingPlotChart == "ParalleGram" ||
          chart.addingPlotChart == "AngleLine" ||
          chart.addingPlotChart == "Parallel" ||
          chart.addingPlotChart == "SymmetricTriangle") {
        int eIndex = touchIndex;
        int bIndex = eIndex - 5;
        if (bIndex >= 0) {
          double fDate = getChartDateByIndex(chart, bIndex);
          double sDate = getChartDateByIndex(chart, eIndex);
          double y = getChartValue(chart, mp);
          FCPlot newPlot = FCPlot();
          if (chart.getPaint().defaultUIStyle == "light") {
            newPlot.lineColor = "rgb(0,0,0)";
            newPlot.pointColor = "rgba(0,0,0,125)";
          } else {
            newPlot.lineColor = "rgb(255,255,255)";
            newPlot.pointColor = "rgba(255,255,255,125)";
          }
          newPlot.key1 = fDate;
          newPlot.value1 = y;
          newPlot.key2 = sDate;
          newPlot.value2 = y;
          newPlot.key3 = sDate;
          newPlot.value3 =
              chart.candleMin + (chart.candleMax - chart.candleMin) / 2;
          newPlot.plotType = chart.addingPlotChart;
          chart.plots.add(newPlot);
          chart.sPlot = <FCPlot>[];
          FCPlot selectedPlot = selectPlot(chart, mp);
          if (selectedPlot.isNotEmpty) {
            chart.setSelectedPlot(selectedPlot);
          }
        }
      } else {
        int eIndex = touchIndex;
        int bIndex = eIndex - 5;
        if (bIndex >= 0) {
          double fDate = getChartDateByIndex(chart, bIndex);
          double sDate = getChartDateByIndex(chart, eIndex);
          double y = getChartValue(chart, mp);
          FCPlot newPlot = FCPlot();
          if (chart.getPaint().defaultUIStyle == "light") {
            newPlot.lineColor = "rgb(0,0,0)";
            newPlot.pointColor = "rgba(0,0,0,125)";
          } else {
            newPlot.lineColor = "rgb(255,255,255)";
            newPlot.pointColor = "rgba(255,255,255,125)";
          }
          newPlot.key1 = fDate;
          newPlot.value1 = y;
          newPlot.key2 = sDate;
          newPlot.value2 = y;
          newPlot.plotType = chart.addingPlotChart;
          chart.plots.add(newPlot);
          chart.sPlot = <FCPlot>[];
          FCPlot selectedPlot = selectPlot(chart, mp);
          if (selectedPlot.isNotEmpty) {
            chart.setSelectedPlot(selectedPlot);
          }
        }
      }
    }
  }
}

/*
* 图表的鼠标按下方法
* chart: 图表
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
*/
void touchDownChart(FCChart chart, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint) {
  chart.touchDownPointChart = firstPoint;
  chart.crossStopIndex = getChartIndex(chart, firstPoint);
  chart.offsetX = 0;
	chart.targetOldX = 0;
	chart.targetOldX2 = 0;
  chart.selectShape = "";
  chart.selectShapeEx = "";
  chart.sPlot = <FCPlot>[];
  FCPlot selectedPlot = selectPlot(chart, firstPoint);
  if (selectedPlot.isNotEmpty) {
    chart.setSelectedPlot(selectedPlot);
  }
  if (!chart.getSelectedPlot().isNotEmpty) {
    selectShape(chart, firstPoint);
  }
  if(chart.getPaint().isDoubleClick){
		chart.showCrossLine = !chart.showCrossLine;
	}
}

/*
*左滚
*chart:图表
*step:步长
*/
void scrollLeftChart(FCChart chart, int step){
	chart.offsetX = 0;
	chart.targetOldX = 0;
	chart.targetOldX2 = 0;
	if(chart.showCrossLine){
		chart.crossStopIndex = chart.crossStopIndex - step;
		if(chart.crossStopIndex >= chart.firstVisibleIndex){
			step = 0;
		}
		else if(chart.crossStopIndex < 0){
			chart.crossStopIndex = 0;
		}
	}
	if(step > 0){
		int subIndex = chart.lastVisibleIndex - chart.firstVisibleIndex;
		int fIndex = chart.firstVisibleIndex - step;
		if(fIndex < 0){
			fIndex = 0;
		}
		int eIndex = fIndex + subIndex;
		chart.firstVisibleIndex = fIndex;
		chart.lastVisibleIndex = eIndex;
	}
	checkChartLastVisibleIndex(chart);
	if (chart.getPaint().hasCalculateChartMaxMinEvent) {
    chart.getPaint().onCalculateChartMaxMin(chart);
  } else {
    calculateChartMaxMin(chart);
  }
}

/*
*右滚
*chart:图表
*step:步长
*/
void scrollRightChart(FCChart chart, int step){
	chart.offsetX = 0;
	chart.targetOldX = 0;
	chart.targetOldX2 = 0;
	int dataCount = chart.data.length;
	if(chart.showCrossLine){
		chart.crossStopIndex = chart.crossStopIndex + step;
		if(chart.crossStopIndex <= chart.lastVisibleIndex){
			step = 0;
		}
		else if(chart.crossStopIndex > dataCount - 1){
			chart.crossStopIndex = dataCount - 1;
		}
	}
	if(step > 0){
		int subIndex = chart.lastVisibleIndex - chart.firstVisibleIndex;
		int eIndex = chart.lastVisibleIndex + step;
		if(eIndex > dataCount - 1){
			eIndex = dataCount - 1;
		}
		int fIndex = eIndex - subIndex;
		chart.firstVisibleIndex = fIndex;
		chart.lastVisibleIndex = eIndex;
	}
	checkChartLastVisibleIndex(chart);
	if (chart.getPaint().hasCalculateChartMaxMinEvent) {
    chart.getPaint().onCalculateChartMaxMin(chart);
  } else {
    calculateChartMaxMin(chart);
  }
}

/*
*图表的键盘按下事件
*chart:图表
*key:按键
*/
void keyDownChart(FCChart chart, int key){
	if (key == 38){
		zoomOutChart(chart);
	}else if(key == 40){
		zoomInChart(chart);
	}else if(key == 37){
		scrollLeftChart(chart, 1);
	}else if(key == 39){
		scrollRightChart(chart, 1);
	}
}

/*
* 图表的鼠标移动方法
* chart: 图表
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
*/
void touchMoveChart(FCChart chart, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint) {
  if (!chart.data.isNotEmpty) {
    return;
  }
  FCPoint mp = firstPoint;
  chart.crossStopIndex = getChartIndex(chart, mp);
  chart.touchPosition = mp;
  chart.offsetX = 0;
	chart.targetOldX = 0;
	chart.targetOldX2 = 0;
  FCPlot sPlot = chart.getSelectedPlot();
  if (firstTouch && sPlot.isNotEmpty) {
    int newIndex = getChartIndex(chart, mp);
    if (newIndex >= 0 && newIndex < chart.data.length) {
      double newDate = getChartDateByIndex(chart, newIndex);
      double newValue = getCandleDivValue(chart, mp);
      if (chart.selectPlotPoint == 0) {
        sPlot.key1 = newDate;
        sPlot.value1 = newValue;
      } else if (chart.selectPlotPoint == 1) {
        sPlot.key2 = newDate;
        sPlot.value2 = newValue;
      } else if (chart.selectPlotPoint == 2) {
        sPlot.key3 = newDate;
        sPlot.value3 = newValue;
      } else if (chart.startMovePlot) {
        double bValue = getCandleDivValue(chart, chart.touchDownPointChart);
        int bIndex = getChartIndex(chart, chart.touchDownPointChart);
        if (sPlot.key1 > 0) {
          sPlot.value1 = sPlot.startValue1 + (newValue - bValue);
          int startIndex1 = getChartIndexByDate(chart, sPlot.startKey1);
          int newIndex1 = startIndex1 + (newIndex - bIndex);
          if (newIndex1 < 0) {
            newIndex1 = 0;
          } else if (newIndex1 > chart.data.length - 1) {
            newIndex1 = chart.data.length - 1;
          }
          sPlot.key1 = getChartDateByIndex(chart, newIndex1);
        }
        if (sPlot.key2 > 0) {
          sPlot.value2 = sPlot.startValue2 + (newValue - bValue);
          int startIndex2 = getChartIndexByDate(chart, sPlot.startKey2);
          int newIndex2 = startIndex2 + (newIndex - bIndex);
          if (newIndex2 < 0) {
            newIndex2 = 0;
          } else if (newIndex2 > chart.data.length - 1) {
            newIndex2 = chart.data.length - 1;
          }
          sPlot.key2 = getChartDateByIndex(chart, newIndex2);
        }
        if (sPlot.key3 > 0) {
          sPlot.value3 = sPlot.startValue3 + (newValue - bValue);
          int startIndex3 = getChartIndexByDate(chart, sPlot.startKey3);
          int newIndex3 = startIndex3 + (newIndex - bIndex);
          if (newIndex3 < 0) {
            newIndex3 = 0;
          } else if (newIndex3 > chart.data.length - 1) {
            newIndex3 = chart.data.length - 1;
          }
          sPlot.key3 = getChartDateByIndex(chart, newIndex3);
        }
      }
    }
    return;
  }
  if (firstTouch && secondTouch) {
    if (firstPoint.x > secondPoint.x) {
      chart.firstTouchPointCacheChart = secondPoint;
      chart.secondTouchPointCacheChart = firstPoint;
    } else {
      chart.firstTouchPointCacheChart = firstPoint;
      chart.secondTouchPointCacheChart = secondPoint;
    }
    if (chart.firstTouchIndexCacheChart == -1 ||
        chart.secondTouchIndexCacheChart == -1) {
      chart.firstTouchIndexCacheChart =
          getChartIndex(chart, chart.firstTouchPointCacheChart);
      chart.secondTouchIndexCacheChart =
          getChartIndex(chart, chart.secondTouchPointCacheChart);
      chart.firstIndexCacheChart = chart.firstVisibleIndex;
      chart.lastIndexCacheChart = chart.lastVisibleIndex;
    }
  } else if (firstTouch) {
    chart.secondTouchIndexCacheChart = -1;
    if (chart.firstTouchIndexCacheChart == -1) {
      chart.firstTouchPointCacheChart = firstPoint;
      chart.firstTouchIndexCacheChart =
          getChartIndex(chart, chart.firstTouchPointCacheChart);
      chart.firstIndexCacheChart = chart.firstVisibleIndex;
      chart.lastIndexCacheChart = chart.lastVisibleIndex;
      chart.firstPaddingTop = chart.candlePaddingTop;
      chart.firtstPaddingBottom = chart.candlePaddingBottom;
    }
  }

  if (firstTouch && secondTouch) {
    if (chart.firstTouchIndexCacheChart != -1 &&
        chart.secondTouchIndexCacheChart != -1) {
      FCPoint fPoint = firstPoint;
      FCPoint sPoint = secondPoint;
      if (firstPoint.x > secondPoint.x) {
        fPoint = secondPoint;
        sPoint = firstPoint;
      }
      double subX = (sPoint.x - fPoint.x).abs();
      int subIndex =
          (chart.secondTouchIndexCacheChart - chart.firstTouchIndexCacheChart)
              .abs();
      if (subX > 0 && subIndex > 0) {
        double newScalePixel = subX / subIndex;
        if (newScalePixel >= 3) {
          double intScalePixel = newScalePixel.toInt().toDouble();
          newScalePixel = intScalePixel;
        }
        if (newScalePixel != chart.hScalePixel) {
          int newFirstIndex = chart.firstTouchIndexCacheChart;
          double thisX = fPoint.x;
          thisX -= newScalePixel;
          while (thisX > chart.leftVScaleWidth + newScalePixel) {
            newFirstIndex--;
            if (newFirstIndex < 0) {
              newFirstIndex = 0;
              break;
            }
            thisX -= newScalePixel;
          }

          thisX = sPoint.x;
          int newSecondIndex = chart.secondTouchIndexCacheChart;
          thisX += newScalePixel;
          while (
              thisX < chart.size.cx - chart.rightVScaleWidth - newScalePixel) {
            newSecondIndex++;
            if (newSecondIndex > chart.data.length - 1) {
              newSecondIndex = chart.data.length - 1;
              break;
            }
            thisX += newScalePixel;
          }
          setChartVisibleIndex(chart, newFirstIndex, newSecondIndex);
          int maxVisibleRecord = getChartMaxVisibleCount(
              chart, chart.hScalePixel, getChartWorkAreaWidth(chart));
          while (maxVisibleRecord <
                  chart.lastVisibleIndex - chart.firstVisibleIndex + 1 &&
              chart.lastVisibleIndex > chart.firstVisibleIndex) {
            chart.lastVisibleIndex--;
          }
          checkChartLastVisibleIndex(chart);
          resetChartVisibleRecord(chart);
          if (chart.getPaint().hasCalculateChartMaxMinEvent) {
            chart.getPaint().onCalculateChartMaxMin(chart);
          } else {
            calculateChartMaxMin(chart);
          }
        }
      }
    }
  } else if (firstTouch) {
    int subIndex =
        (chart.firstTouchPointCacheChart.x - firstPoint.x) ~/ chart.hScalePixel;
    if (chart.allowDragChartDiv) {
      chart.candlePaddingTop = chart.firstPaddingTop -
          (chart.firstTouchPointCacheChart.y - firstPoint.y);
      chart.candlePaddingBottom = chart.firtstPaddingBottom +
          (chart.firstTouchPointCacheChart.y - firstPoint.y);
    }
    chart.firstVisibleIndex = chart.firstIndexCacheChart + subIndex;
    chart.lastVisibleIndex = chart.lastIndexCacheChart + subIndex;
    checkChartLastVisibleIndex(chart);
    if (chart.getPaint().hasCalculateChartMaxMinEvent) {
      chart.getPaint().onCalculateChartMaxMin(chart);
    } else {
      calculateChartMaxMin(chart);
    }
  }
}

/*
* 计算线性回归上下限
* chart:图表
* plot:画线
* a:直线k
* b:直线b
*/
void getLRBandRange(FCChart chart, FCPlot plot, double a, double b) {
  int bIndex = getChartIndexByDate(chart, plot.key1);
  int eIndex = getChartIndexByDate(chart, plot.key2);
  int tempBIndex = min(bIndex, eIndex);
  int tempEIndex = max(bIndex, eIndex);
  bIndex = tempBIndex;
  eIndex = tempEIndex;
  List<double> upList = <double>[];
  List<double> downList = <double>[];
  for (int i = bIndex; i <= eIndex; i++) {
    double high = chart.data[i].high;
    double low = chart.data[i].low;
    double midValue = (i - bIndex + 1) * a + b;
    upList.add(high - midValue);
    downList.add(midValue - low);
  }
  chart.upSubValue = maxValue(upList);
  chart.downSubValue = maxValue(downList);
}

/*
* 根据位置计算索引
* chart:图表
* mp:坐标
*/
int getChartIndex(FCChart chart, FCPoint mp) {
  if (!chart.data.isNotEmpty) {
    return -1;
  }
  if (mp.x <= 0) {
    return 0;
  }
  double intX = mp.x - chart.leftVScaleWidth - chart.hScalePixel - chart.offsetX;
  int index = (chart.firstVisibleIndex + intX / chart.hScalePixel).toInt();
  if (intX % chart.hScalePixel != 0) {
    index++;
  }
  if (index < 0) {
    index = 0;
  } else if (index > chart.data.length - 1) {
    index = chart.data.length - 1;
  }
  return index;
}

/*
* 获取最大显示记录条数
* chart:图表
* hScalePixel:间隔
* pureH:横向距离
*/
int getChartMaxVisibleCount(FCChart chart, double hScalePixel, double pureH) {
  int count = (pureH - hScalePixel) ~/ hScalePixel;
  if (count < 0) {
    count = 0;
  }
  return count;
}

/*
* 获取图表层的高度
* chart:图表
*/
double getCandleDivHeight(FCChart chart) {
  double height = chart.size.cy - chart.hScaleHeight;
  if (height > 0) {
    return height * chart.candleDivPercent;
  } else {
    return 0;
  }
}

/*
* 获取成交量层的高度
* chart:图表
*/
double getVolDivHeight(FCChart chart) {
  double height = chart.size.cy - chart.hScaleHeight;
  if (height > 0) {
    return height * chart.volDivPercent;
  } else {
    return 0;
  }
}

/*
* 获取指标层的高度
* chart:图表
*/
double getIndDivHeight(FCChart chart) {
  double height = chart.size.cy - chart.hScaleHeight;
  if (height > 0) {
    return height * chart.indDivPercent;
  } else {
    return 0;
  }
}

/*
* 获取指标层2的高度
* chart:图表
*/
double getIndDivHeight2(FCChart chart) {
  double height = chart.size.cy - chart.hScaleHeight;
  if (height > 0) {
    return height * chart.indDivPercent2;
  } else {
    return 0;
  }
}

/*
* 获取横向工作区
* chart:图表
*/
double getChartWorkAreaWidth(FCChart chart) {
  return chart.size.cx -
      chart.leftVScaleWidth -
      chart.rightVScaleWidth -
      chart.rightSpace;
}

/*
* 根据索引获取横坐标
* chart:图表
* index:索引
*/
double getChartX(FCChart chart, int index) {
  return chart.leftVScaleWidth +
      (index - chart.firstVisibleIndex) * chart.hScalePixel +
      chart.hScalePixel + chart.offsetX;
}

/*
* 根据日期获取索引
* chart:图表
* date:日期
*/
int getChartIndexByDate(FCChart chart, double date) {
  int index = -1;
  for (int i = 0; i < chart.data.length; i++) {
    if (chart.data[i].date == date) {
      index = i;
      break;
    }
  }
  return index;
}

/*
* 根据索引获取日期
* chart:图表
* index:索引
*/
double getChartDateByIndex(FCChart chart, int index) {
  double date = 0;
  if (index >= 0 && index < chart.data.length) {
    date = chart.data[index].date;
  }
  return date;
}

/*
* 计算数值在层中的位置
* chart:图表
* divIndex:所在层
* chart:数值
*/
double getChartY(FCChart chart, int divIndex, double value) {
  if (divIndex == 0) {
    if (chart.candleMax > chart.candleMin) {
      double cValue = value, cMax = chart.candleMax, cMin = chart.candleMin;
      if (chart.vScaleType != "standard") {
        if (cValue > 0) {
          cValue = log(cValue);
        } else if (cValue < 0) {
          cValue = -log(cValue.abs());
        }
        if (cMax > 0) {
          cMax = log(cMax);
        } else if (cMax < 0) {
          cMax = -log(cMax.abs());
        }
        if (cMin > 0) {
          cMin = log(cMin);
        } else if (cMin < 0) {
          cMin = -log(cMin.abs());
        }
      }
      double rate = (cValue - cMin) / (cMax - cMin);
      double divHeight = getCandleDivHeight(chart);
      return divHeight -
          chart.candlePaddingBottom -
          (divHeight - chart.candlePaddingTop - chart.candlePaddingBottom) *
              rate;
    } else {
      return 0;
    }
  } else if (divIndex == 1) {
    if (chart.volMax > chart.volMin) {
      double rate = (value - chart.volMin) / (chart.volMax - chart.volMin);
      double candleHeight = getCandleDivHeight(chart);
      double volHeight = getVolDivHeight(chart);
      return candleHeight +
          volHeight -
          chart.volPaddingBottom -
          (volHeight - chart.volPaddingTop - chart.volPaddingBottom) * rate;
    } else {
      return 0;
    }
  } else if (divIndex == 2) {
    if (chart.indMax > chart.indMin) {
      double rate = (value - chart.indMin) / (chart.indMax - chart.indMin);
      double candleHeight = getCandleDivHeight(chart);
      double volHeight = getVolDivHeight(chart);
      double indHeight = getIndDivHeight(chart);
      return candleHeight +
          volHeight +
          indHeight -
          chart.indPaddingBottom -
          (indHeight - chart.indPaddingTop - chart.indPaddingBottom) * rate;
    } else {
      return 0;
    }
  } else if (divIndex == 3) {
    if (chart.indMax2 > chart.indMin2) {
      double rate = (value - chart.indMin2) / (chart.indMax2 - chart.indMin2);
      double candleHeight = getCandleDivHeight(chart);
      double volHeight = getVolDivHeight(chart);
      double indHeight = getIndDivHeight(chart);
      double indHeight2 = getIndDivHeight2(chart);
      return candleHeight +
          volHeight +
          indHeight +
          indHeight2 -
          chart.indPaddingBottom2 -
          (indHeight2 - chart.indPaddingTop2 - chart.indPaddingBottom2) * rate;
    } else {
      return 0;
    }
  }
  return 0;
}

/*
* 计算数值在层中的右轴位置
* chart:图表
* divIndex:所在层
* chart:数值
*/
double getChartYInRight(FCChart chart, int divIndex, double value) {
  if (divIndex == 0) {
    if (chart.candleMaxRight > chart.candleMinRight) {
      double cValue = value,
          cMax = chart.candleMaxRight,
          cMin = chart.candleMinRight;
      if (chart.vScaleType != "standard") {
        if (cValue > 0) {
          cValue = log(cValue);
        } else if (cValue < 0) {
          cValue = -log(cValue.abs());
        }
        if (cMax > 0) {
          cMax = log(cMax);
        } else if (cMax < 0) {
          cMax = -log(cMax.abs());
        }
        if (cMin > 0) {
          cMin = log(cMin);
        } else if (cMin < 0) {
          cMin = -log(cMin.abs());
        }
      }
      double rate = (cValue - cMin) / (cMax - cMin);
      double divHeight = getCandleDivHeight(chart);
      return divHeight -
          chart.candlePaddingBottom -
          (divHeight - chart.candlePaddingTop - chart.candlePaddingBottom) *
              rate;
    } else {
      return 0;
    }
  } else if (divIndex == 1) {
    if (chart.volMaxRight > chart.volMinRight) {
      double rate =
          (value - chart.volMinRight) / (chart.volMaxRight - chart.volMinRight);
      double candleHeight = getCandleDivHeight(chart);
      double volHeight = getVolDivHeight(chart);
      return candleHeight +
          volHeight -
          chart.volPaddingBottom -
          (volHeight - chart.volPaddingTop - chart.volPaddingBottom) * rate;
    } else {
      return 0;
    }
  } else if (divIndex == 2) {
    if (chart.indMaxRight > chart.indMinRight) {
      double rate =
          (value - chart.indMinRight) / (chart.indMaxRight - chart.indMinRight);
      double candleHeight = getCandleDivHeight(chart);
      double volHeight = getVolDivHeight(chart);
      double indHeight = getIndDivHeight(chart);
      return candleHeight +
          volHeight +
          indHeight -
          chart.indPaddingBottom -
          (indHeight - chart.indPaddingTop - chart.indPaddingBottom) * rate;
    } else {
      return 0;
    }
  } else if (divIndex == 3) {
    if (chart.indMax2Right > chart.indMin2Right) {
      double rate = (value - chart.indMin2Right) /
          (chart.indMax2Right - chart.indMin2Right);
      double candleHeight = getCandleDivHeight(chart);
      double volHeight = getVolDivHeight(chart);
      double indHeight = getIndDivHeight(chart);
      double indHeight2 = getIndDivHeight2(chart);
      return candleHeight +
          volHeight +
          indHeight +
          indHeight2 -
          chart.indPaddingBottom2 -
          (indHeight2 - chart.indPaddingTop2 - chart.indPaddingBottom2) * rate;
    } else {
      return 0;
    }
  }
  return 0;
}

/*
* 根据坐标获取对应的值
* chart:图表
* point:坐标
*/
double getChartValue(FCChart chart, FCPoint point) {
  double candleHeight = getCandleDivHeight(chart);
  double volHeight = getVolDivHeight(chart);
  double indHeight = getIndDivHeight(chart);
  double indHeight2 = getIndDivHeight(chart);
  if (point.y <= candleHeight) {
    double rate = (candleHeight - chart.candlePaddingBottom - point.y) /
        (candleHeight - chart.candlePaddingTop - chart.candlePaddingBottom);
    double cMin = chart.candleMin, cMax = chart.candleMax;
    if (chart.vScaleType != "standard") {
      if (cMax > 0) {
        cMax = log(cMax);
      } else if (cMax < 0) {
        cMax = -log(cMax.abs());
      }
      if (cMin > 0) {
        cMin = log(cMin);
      } else if (cMin < 0) {
        cMin = -log(cMin.abs());
      }
    }
    double result = cMin + (cMax - cMin) * rate;
    if (chart.vScaleType != "standard") {
      return pow(10, result).toDouble();
    } else {
      return result;
    }
  } else if (point.y > candleHeight && point.y <= candleHeight + volHeight) {
    double rate =
        (volHeight - chart.volPaddingBottom - (point.y - candleHeight)) /
            (volHeight - chart.volPaddingTop - chart.volPaddingBottom);
    return chart.volMin + (chart.volMax - chart.volMin) * rate;
  } else if (point.y > candleHeight + volHeight &&
      point.y <= candleHeight + volHeight + indHeight) {
    double rate = (indHeight -
            chart.indPaddingBottom -
            (point.y - candleHeight - volHeight)) /
        (indHeight - chart.indPaddingTop - chart.indPaddingBottom);
    return chart.indMin + (chart.indMax - chart.indMin) * rate;
  } else if (point.y > candleHeight + volHeight + indHeight &&
      point.y <= candleHeight + volHeight + indHeight + indHeight2) {
    double rate = (indHeight2 -
            chart.indPaddingBottom2 -
            (point.y - candleHeight - volHeight - indHeight)) /
        (indHeight2 - chart.indPaddingTop2 - chart.indPaddingBottom2);
    return chart.indMin2 + (chart.indMax2 - chart.indMin2) * rate;
  }
  return 0;
}

/*
* 根据坐标获取对应的值
* chart:图表
* point:坐标
*/
double getCandleDivValue(FCChart chart, FCPoint point) {
  double candleHeight = getCandleDivHeight(chart);
  double rate = (candleHeight - chart.candlePaddingBottom - point.y) /
      (candleHeight - chart.candlePaddingTop - chart.candlePaddingBottom);
  double cMin = chart.candleMin, cMax = chart.candleMax;
  if (chart.vScaleType != "standard") {
    if (cMax > 0) {
      cMax = log(cMax);
    } else if (cMax < 0) {
      cMax = -log(cMax.abs());
    }
    if (cMin > 0) {
      cMin = log(cMin);
    } else if (cMin < 0) {
      cMin = -log(cMin.abs());
    }
  }
  double result = cMin + (cMax - cMin) * rate;
  if (chart.vScaleType != "standard") {
    return pow(10, result).toDouble();
  } else {
    return result;
  }
}

/*
* 检查最后可见索引
* chart:图表
*/
void checkChartLastVisibleIndex(FCChart chart) {
  if (chart.data.isNotEmpty){
  int dataCount = chart.data.length;
		double workingAreaWidth = getChartWorkAreaWidth(chart);
		int maxVisibleRecord = getChartMaxVisibleCount(chart, chart.hScalePixel, workingAreaWidth);
		if(chart.firstVisibleIndex < 0){
			chart.firstVisibleIndex = 0;
		}
		if(chart.lastVisibleIndex >= chart.firstVisibleIndex + maxVisibleRecord - 1 || chart.lastVisibleIndex < dataCount - 1){
			chart.lastVisibleIndex = chart.firstVisibleIndex + maxVisibleRecord - 1;
		}
		if(chart.lastVisibleIndex > dataCount - 1){
			chart.lastVisibleIndex = dataCount - 1;
		}
}
  if (chart.data.isNotEmpty && chart.lastVisibleIndex != -1) {
    chart.lastVisibleKey = chart.data[chart.lastVisibleIndex].date;
    if (chart.lastVisibleIndex == chart.data.length - 1) {
      chart.lastRecordIsVisible = true;
    } else {
      chart.lastRecordIsVisible = false;
    }
  } else {
    chart.lastVisibleKey = 0;
    chart.lastRecordIsVisible = true;
  }
}

/*
* 自动设置首先可见和最后可见的记录号
* chart:图表
*/
void resetChartVisibleRecord(FCChart chart) {
  int rowsCount = chart.data.length;
  double workingAreaWidth = getChartWorkAreaWidth(chart);
  if (chart.autoFillHScale) {
    if (workingAreaWidth > 0 && rowsCount > 0) {
      chart.hScalePixel = workingAreaWidth / rowsCount;
      chart.firstVisibleIndex = 0;
      chart.lastVisibleIndex = rowsCount - 1;
    }
  } else {
    int maxVisibleRecord =
        getChartMaxVisibleCount(chart, chart.hScalePixel, workingAreaWidth);
    //没数据时重置
    if (rowsCount == 0) {
      chart.firstVisibleIndex = -1;
      chart.lastVisibleIndex = -1;
    } else {
      //数据不足一屏时
      if (rowsCount < maxVisibleRecord) {
        chart.lastVisibleIndex = rowsCount - 1;
        chart.firstVisibleIndex = 0;
      }
      //数据超过一屏时
      else {
        //显示中间的数据时
        if (chart.firstVisibleIndex != -1 &&
            chart.lastVisibleIndex != -1 &&
            !chart.lastRecordIsVisible) {
          int index = getChartIndexByDate(chart, chart.lastVisibleKey);
          if (index != -1) {
            chart.lastVisibleIndex = index;
          }
          chart.firstVisibleIndex =
              chart.lastVisibleIndex - maxVisibleRecord + 1;
          if (chart.firstVisibleIndex < 0) {
            chart.firstVisibleIndex = 0;
            chart.lastVisibleIndex = chart.firstVisibleIndex + maxVisibleRecord;
            checkChartLastVisibleIndex(chart);
          }
        } else {
          //第一条或最后一条数据被显示时
          chart.lastVisibleIndex = rowsCount - 1;
          chart.firstVisibleIndex =
              chart.lastVisibleIndex - maxVisibleRecord + 1;
          if (chart.firstVisibleIndex > chart.lastVisibleIndex) {
            chart.firstVisibleIndex = chart.lastVisibleIndex;
          }
        }
      }
    }
  }
}

/*
* 设置可见索引
* chart:图表
* firstVisibleIndex:起始索引
* lastVisibleIndex:结束索引
*/
void setChartVisibleIndex(
    FCChart chart, int firstVisibleIndex, int lastVisibleIndex) {
  double xScalePixel =
      getChartWorkAreaWidth(chart) / (lastVisibleIndex - firstVisibleIndex + 1);
  if (xScalePixel < 1000000) {
    chart.firstVisibleIndex = firstVisibleIndex;
    chart.lastVisibleIndex = lastVisibleIndex;
    //设置最后一条记录是否可见
    if (lastVisibleIndex != chart.data.length - 1) {
      chart.lastRecordIsVisible = false;
    } else {
      chart.lastRecordIsVisible = true;
    }
    chart.hScalePixel = xScalePixel;
    checkChartLastVisibleIndex(chart);
  }
}

/*
* 清除图形
* chart:图表
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawChart(FCChart chart, FCPaint paint, FCRect clipRect) {
  if (chart.backColor != "none") {
    paint.fillRect(chart.backColor, 0, 0, chart.size.cx, chart.size.cy);
  }
  if (paint.hasPaintChartScaleEvent) {
    paint.onPaintChartScale(chart, paint, clipRect);
  } else {
    drawChartScale(chart, paint, clipRect);
  }
  if (paint.hasPaintChartStockEvent) {
    paint.onPaintChartStock(chart, paint, clipRect);
  } else {
    drawChartStock(chart, paint, clipRect);
  }
  if (paint.hasPaintChartPlotEvent) {
    paint.onPaintChartPlot(chart, paint, clipRect);
  } else {
    drawChartPlot(chart, paint, clipRect);
  }
  if (paint.hasPaintChartCrossLineEvent) {
    paint.onPaintChartCrossLine(chart, paint, clipRect);
  } else {
    drawChartCrossLine(chart, paint, clipRect);
  }
  if (chart.borderColor != "none") {
    paint.drawRect(chart.borderColor, chart.lineWidthChart, 0, 0, 0,
        chart.size.cx, chart.size.cy);
  }
}

/*
* 计算坐标轴
* min:最小值
* max:最大值
* yLen:长度
* maxSpan:最大间隔
* minSpan:最小间隔
* defCount:数量
*/
double chartGridScale(FCChart chart, double min, double max, int yLen,
    double maxSpan, double minSpan, int defCount) {
  double sub = max - min;
  int nMinCount = (yLen / maxSpan).ceil().toInt();
  int nMaxCount = (yLen / minSpan).floor().toInt();
  int nCount = defCount;
  double logStep = sub / nCount;
  bool start = false;
  double divisor = 0;
  int i = 0, nTemp = 0;
  chart.gridStepChart = 0;
  chart.gridDigitChart = 0;
  nCount = nMinCount > nCount ? nMinCount : nCount;
  nCount = nMaxCount > nCount ? nCount : nMaxCount;
  nCount = nCount > 1 ? nCount : 1;
  for (i = 15; i >= -6; i--) {
    divisor = pow(10, i).toDouble();
    if (divisor < 1) {
      chart.gridDigitChart++;
    }
    nTemp = (logStep / divisor).floor().toInt();
    if (start) {
      if (nTemp < 4) {
        if (chart.gridDigitChart > 0) {
          chart.gridDigitChart--;
        }
      } else if (nTemp >= 4 && nTemp <= 6) {
        nTemp = 5;
        chart.gridStepChart += nTemp * divisor;
      } else {
        chart.gridStepChart += 10 * divisor;
        if (chart.gridDigitChart > 0) {
          chart.gridDigitChart--;
        }
      }
      break;
    } else if (nTemp > 0) {
      chart.gridStepChart = nTemp * divisor + chart.gridStepChart;
      logStep -= chart.gridStepChart;
      start = true;
    }
  }
  return 0;
}

/*
* 缩小
* chart:图表
*/
void zoomOutChart(FCChart chart) {
  if (!chart.autoFillHScale) {
    double hScalePixel = chart.hScalePixel;
    double oldX = getChartX(chart, chart.crossStopIndex);
    if(chart.showCrossLine && chart.targetOldX == 0){
			chart.targetOldX = oldX;
		}
    double pureH = getChartWorkAreaWidth(chart);
    int oriMax = -1, max = -1, deal = 0;
    int dataCount = chart.data.length;
    int findex = chart.firstVisibleIndex, lindex = chart.lastVisibleIndex;
    if (hScalePixel < pureH) {
      oriMax = getChartMaxVisibleCount(chart, hScalePixel, pureH);
      if (dataCount < oriMax) {
        deal = 1;
      }
      if (hScalePixel > 3) {
        hScalePixel += 1;
      } else {
        if (hScalePixel == 1) {
          hScalePixel = 2;
        } else {
          hScalePixel = hScalePixel * 1.5;
          if (hScalePixel > 3) {
            hScalePixel = hScalePixel.toInt().toDouble();
          }
        }
      }
      max = getChartMaxVisibleCount(chart, hScalePixel, pureH);
      if (dataCount >= max) {
        if (deal == 1) {
          lindex = dataCount - 1;
        }
        findex = lindex - max + 1;
        if (findex < 0) {
          findex = 0;
        }
      }
    }
    chart.hScalePixel = hScalePixel;
    if(chart.showCrossLine){
			double sX = chart.targetOldX;
			findex = chart.crossStopIndex;
			while(sX >= chart.leftVScaleWidth + chart.hScalePixel){
				findex = findex - 1;
				chart.offsetX = sX - chart.leftVScaleWidth - chart.hScalePixel;
				sX = sX - chart.hScalePixel;
			}
			findex = findex + 1;
		}
    chart.firstVisibleIndex = findex;
    chart.lastVisibleIndex = lindex;
    checkChartLastVisibleIndex(chart);
    if (chart.getPaint().hasCalculateChartMaxMinEvent) {
      chart.getPaint().onCalculateChartMaxMin(chart);
    } else {
      calculateChartMaxMin(chart);
    }
  }
}

/*
* 放大
* chart:图表
*/
void zoomInChart(FCChart chart) {
  if (!chart.autoFillHScale) {
    double hScalePixel = chart.hScalePixel;
    double oldX = getChartX(chart, chart.crossStopIndex);
    if(chart.showCrossLine && chart.targetOldX2 == 0){
			chart.targetOldX2 = oldX;
		}
    double pureH = getChartWorkAreaWidth(chart);
    int max = -1;
    int dataCount = chart.data.length;
    int findex = chart.firstVisibleIndex, lindex = chart.lastVisibleIndex;
    if (hScalePixel > 3) {
      hScalePixel -= 1;
    } else {
      hScalePixel = hScalePixel * 2 / 3;
      if (hScalePixel > 3) {
        hScalePixel = hScalePixel.toInt().toDouble();
      }
    }
    max = getChartMaxVisibleCount(chart, hScalePixel, pureH);
    if (max >= dataCount) {
      if (hScalePixel < 1) {
        hScalePixel = pureH / max;
      }
      findex = 0;
      lindex = dataCount - 1;
    } else {
      findex = lindex - max + 1;
      if (findex < 0) {
        findex = 0;
      }
    }
    chart.hScalePixel = hScalePixel;
    if(chart.showCrossLine){
			double sX = chart.targetOldX;
			findex = chart.crossStopIndex;
			while(sX >= chart.leftVScaleWidth + chart.hScalePixel){
				findex = findex - 1;
				chart.offsetX = sX - chart.leftVScaleWidth - chart.hScalePixel;
				sX = sX - chart.hScalePixel;
			}
			findex = findex + 1;
		}
    chart.firstVisibleIndex = findex;
    chart.lastVisibleIndex = lindex;
    checkChartLastVisibleIndex(chart);
    if (chart.getPaint().hasCalculateChartMaxMinEvent) {
      chart.getPaint().onCalculateChartMaxMin(chart);
    } else {
      calculateChartMaxMin(chart);
    }
  }
}

/*
* 绘制刻度
* chart:图表
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawChartScale(FCChart chart, FCPaint paint, FCRect clipRect) {
  if (chart.leftVScaleWidth > 0) {
    paint.drawLine(
        chart.scaleColor,
        chart.lineWidthChart,
        0,
        chart.leftVScaleWidth,
        0,
        chart.leftVScaleWidth,
        chart.size.cy - chart.hScaleHeight);
  }
  if (chart.rightVScaleWidth > 0) {
    paint.drawLine(
        chart.scaleColor,
        chart.lineWidthChart,
        0,
        chart.size.cx - chart.rightVScaleWidth,
        0,
        chart.size.cx - chart.rightVScaleWidth,
        chart.size.cy - chart.hScaleHeight);
  }
  if (chart.hScaleHeight > 0) {
    paint.drawLine(
        chart.scaleColor,
        chart.lineWidthChart,
        0,
        0,
        chart.size.cy - chart.hScaleHeight,
        chart.size.cx,
        chart.size.cy - chart.hScaleHeight);
  }
  double candleDivHeight = getCandleDivHeight(chart);
  double volDivHeight = getVolDivHeight(chart);
  double indDivHeight = getIndDivHeight(chart);
  double indDivHeight2 = getIndDivHeight2(chart);
  if (volDivHeight > 0) {
    paint.drawLine(
        chart.scaleColor,
        chart.lineWidthChart,
        0,
        chart.leftVScaleWidth,
        candleDivHeight,
        chart.size.cx - chart.rightVScaleWidth,
        candleDivHeight);
  }
  if (indDivHeight > 0) {
    paint.drawLine(
        chart.scaleColor,
        chart.lineWidthChart,
        0,
        chart.leftVScaleWidth,
        candleDivHeight + volDivHeight,
        chart.size.cx - chart.rightVScaleWidth,
        candleDivHeight + volDivHeight);
  }
  if (indDivHeight2 > 0) {
    paint.drawLine(
        chart.scaleColor,
        chart.lineWidthChart,
        0,
        chart.leftVScaleWidth,
        candleDivHeight + volDivHeight + indDivHeight,
        chart.size.cx - chart.rightVScaleWidth,
        candleDivHeight + volDivHeight + indDivHeight);
  }
  if (chart.data.isNotEmpty) {
    FCPoint topPoint = FCPoint(0, 10);
    FCPoint bottomPoint = FCPoint(0, candleDivHeight - 10);
    double candleMax = getChartValue(chart, topPoint);
    double candleMin = getChartValue(chart, bottomPoint);
    chartGridScale(
        chart,
        candleMin,
        candleMax,
        (candleDivHeight -
                chart.candlePaddingTop -
                chart.candlePaddingBottom) ~/
            2,
        chart.vScaleDistance,
        chart.vScaleDistance / 2,
        (candleDivHeight -
                chart.candlePaddingTop -
                chart.candlePaddingBottom) ~/
            chart.vScaleDistance);
    if (chart.gridStepChart > 0) {
      List<double> drawValues = <double>[];
      bool isTrend = chart.cycle == "trend";
      double firstOpen = 0;
      if (isTrend) {
        firstOpen = chart.data[chart.firstVisibleIndex].close;
        double subValue = (candleMax - candleMin);
        int count = (candleDivHeight -
                chart.candlePaddingTop -
                chart.candlePaddingBottom) ~/
            chart.vScaleDistance;
        if (count > 0) {
          subValue /= count;
        }
        double start = firstOpen;
        while (start < candleMax) {
          start += subValue;
          if (start <= candleMax) {
            drawValues.add(start);
          }
        }
        start = firstOpen;
        while (start > candleMin) {
          start -= subValue;
          if (start >= candleMin) {
            drawValues.add(start);
          }
        }
      } else {
        double start = 0;
        if (candleMin >= 0) {
          while (start + chart.gridStepChart < candleMin) {
            start += chart.gridStepChart;
          }
        } else {
          while (start - chart.gridStepChart > candleMin) {
            start -= chart.gridStepChart;
          }
        }

        while (start <= candleMax) {
          if (start > candleMin) {
            drawValues.add(start);
          }
          start += chart.gridStepChart;
        }
      }
      drawValues.add(firstOpen);
      for (int i = 0; i < drawValues.length; i++) {
        double start = drawValues[i];
        double hAxisY = getChartY(chart, 0, start);
        if (hAxisY < 1 || hAxisY > candleDivHeight) {
          continue;
        }
        paint.drawLine(
            chart.gridColor,
            chart.lineWidthChart,
            0,
            chart.leftVScaleWidth,
            hAxisY,
            chart.size.cx - chart.rightVScaleWidth,
            hAxisY);
        paint.drawLine(chart.scaleColor, chart.lineWidthChart, 0,
            chart.leftVScaleWidth - 8, hAxisY, chart.leftVScaleWidth, hAxisY);
        paint.drawLine(
            chart.scaleColor,
            chart.lineWidthChart,
            0,
            chart.size.cx - chart.rightVScaleWidth,
            hAxisY,
            chart.size.cx - chart.rightVScaleWidth + 8,
            hAxisY);

        String drawText = start.toStringAsFixed(chart.candleDigit);

        FCSize tSize = paint.textSize(drawText, chart.font);
        if (isTrend) {
          double diffRange = ((start - firstOpen) / firstOpen * 100);
          String diffRangeStr = diffRange.toStringAsFixed(2) + "%".toString();
          if (diffRange >= 0) {
            paint.drawText(
                diffRangeStr,
                chart.upColor,
                chart.font,
                chart.size.cx - chart.rightVScaleWidth + 10,
                hAxisY - tSize.cy / 2);
          } else {
            paint.drawText(
                diffRangeStr,
                chart.downColor,
                chart.font,
                chart.size.cx - chart.rightVScaleWidth + 10,
                hAxisY - tSize.cy / 2);
          }
        } else {
          paint.drawText(
              drawText,
              chart.textColor,
              chart.font,
              chart.size.cx - chart.rightVScaleWidth + 10,
              hAxisY - tSize.cy / 2);
        }
        paint.drawText(drawText, chart.textColor, chart.font,
            chart.leftVScaleWidth - tSize.cx - 10, hAxisY - tSize.cy / 2);
      }
    }
    topPoint = FCPoint(0, candleDivHeight + 10);
    bottomPoint = FCPoint(0, candleDivHeight + volDivHeight - 10);
    double volMax = getChartValue(chart, topPoint);
    double volMin = getChartValue(chart, bottomPoint);
    chartGridScale(
        chart,
        volMin,
        volMax,
        (volDivHeight - chart.volPaddingTop - chart.volPaddingBottom) ~/ 2,
        chart.vScaleDistance,
        chart.vScaleDistance / 2,
        (volDivHeight - chart.volPaddingTop - chart.volPaddingBottom) ~/
            chart.vScaleDistance);
    if (chart.gridStepChart > 0) {
      double start = 0;
      if (volMin >= 0) {
        while (start + chart.gridStepChart < volMin) {
          start += chart.gridStepChart;
        }
      } else {
        while (start - chart.gridStepChart > volMin) {
          start -= chart.gridStepChart;
        }
      }
      while (start <= volMax) {
        if (start > volMin) {
          double hAxisY = getChartY(chart, 1, start);
          if (hAxisY < candleDivHeight ||
              hAxisY > candleDivHeight + volDivHeight) {
            continue;
          }
          paint.drawLine(
              chart.gridColor,
              chart.lineWidthChart,
              0,
              chart.leftVScaleWidth,
              hAxisY,
              chart.size.cx - chart.rightVScaleWidth,
              hAxisY);
          paint.drawLine(chart.scaleColor, chart.lineWidthChart, 0,
              chart.leftVScaleWidth - 8, hAxisY, chart.leftVScaleWidth, hAxisY);
          paint.drawLine(
              chart.scaleColor,
              chart.lineWidthChart,
              0,
              chart.size.cx - chart.rightVScaleWidth,
              hAxisY,
              chart.size.cx - chart.rightVScaleWidth + 8,
              hAxisY);
          String drawText =
              (start / chart.magnitude).toStringAsFixed(chart.volDigit);
          FCSize tSize = paint.textSize(drawText, chart.font);
          paint.drawText(
              drawText,
              chart.textColor,
              chart.font,
              chart.size.cx - chart.rightVScaleWidth + 10,
              hAxisY - tSize.cy / 2);
          paint.drawText(drawText, chart.textColor, chart.font,
              chart.leftVScaleWidth - tSize.cx - 10, hAxisY - tSize.cy / 2);
        }
        start += chart.gridStepChart;
      }
    }
    if (indDivHeight > 0) {
      topPoint = FCPoint(0, candleDivHeight + volDivHeight + 10);
      bottomPoint =
          FCPoint(0, candleDivHeight + volDivHeight + indDivHeight - 10);
      double indMax = getChartValue(chart, topPoint);
      double indMin = getChartValue(chart, bottomPoint);
      chartGridScale(
          chart,
          indMin,
          indMax,
          (indDivHeight - chart.indPaddingTop - chart.indPaddingBottom) ~/ 2,
          chart.vScaleDistance,
          chart.vScaleDistance / 2,
          (indDivHeight - chart.indPaddingTop - chart.indPaddingBottom) ~/
              chart.vScaleDistance);
      if (chart.gridStepChart > 0) {
        double start = 0;
        if (indMin >= 0) {
          while (start + chart.gridStepChart < indMin) {
            start += chart.gridStepChart;
          }
        } else {
          while (start - chart.gridStepChart > indMin) {
            start -= chart.gridStepChart;
          }
        }

        while (start <= indMax) {
          if (start > indMin) {
            double hAxisY = getChartY(chart, 2, start);
            if (hAxisY < candleDivHeight + volDivHeight ||
                hAxisY > candleDivHeight + volDivHeight + indDivHeight) {
              continue;
            }
            paint.drawLine(
                chart.gridColor,
                chart.lineWidthChart,
                0,
                chart.leftVScaleWidth,
                hAxisY,
                chart.size.cx - chart.rightVScaleWidth,
                hAxisY);
            paint.drawLine(
                chart.scaleColor,
                chart.lineWidthChart,
                0,
                chart.leftVScaleWidth - 8,
                hAxisY,
                chart.leftVScaleWidth,
                hAxisY);
            paint.drawLine(
                chart.scaleColor,
                chart.lineWidthChart,
                0,
                chart.size.cx - chart.rightVScaleWidth,
                hAxisY,
                chart.size.cx - chart.rightVScaleWidth + 8,
                hAxisY);
            String drawText = start.toStringAsFixed(chart.indDigit);
            FCSize tSize = paint.textSize(drawText, chart.font);
            paint.drawText(
                drawText,
                chart.textColor,
                chart.font,
                chart.size.cx - chart.rightVScaleWidth + 10,
                hAxisY - tSize.cy / 2);
            paint.drawText(drawText, chart.textColor, chart.font,
                chart.leftVScaleWidth - tSize.cx - 10, hAxisY - tSize.cy / 2);
          }
          start += chart.gridStepChart;
        }
      }
    }
    if (indDivHeight2 > 0) {
      topPoint = FCPoint(0, candleDivHeight + volDivHeight + indDivHeight + 10);
      bottomPoint = FCPoint(0,
          candleDivHeight + volDivHeight + indDivHeight + indDivHeight2 - 10);
      double indMax2 = getChartValue(chart, topPoint);
      double indMin2 = getChartValue(chart, bottomPoint);
      chartGridScale(
          chart,
          indMin2,
          indMax2,
          (indDivHeight2 - chart.indPaddingTop2 - chart.indPaddingBottom2) ~/ 2,
          chart.vScaleDistance,
          chart.vScaleDistance / 2,
          (indDivHeight2 - chart.indPaddingTop2 - chart.indPaddingBottom2) ~/
              chart.vScaleDistance);
      if (chart.gridStepChart > 0) {
        double start = 0;
        if (indMin2 >= 0) {
          while (start + chart.gridStepChart < indMin2) {
            start += chart.gridStepChart;
          }
        } else {
          while (start - chart.gridStepChart > indMin2) {
            start -= chart.gridStepChart;
          }
        }

        while (start <= indMax2) {
          if (start > indMin2) {
            double hAxisY = getChartY(chart, 3, start);
            if (hAxisY < candleDivHeight + volDivHeight + indDivHeight ||
                hAxisY >
                    candleDivHeight +
                        volDivHeight +
                        indDivHeight +
                        indDivHeight2) {
              continue;
            }
            paint.drawLine(
                chart.gridColor,
                chart.lineWidthChart,
                0,
                chart.leftVScaleWidth,
                hAxisY,
                chart.size.cx - chart.rightVScaleWidth,
                hAxisY);
            paint.drawLine(
                chart.scaleColor,
                chart.lineWidthChart,
                0,
                chart.leftVScaleWidth - 8,
                hAxisY,
                chart.leftVScaleWidth,
                hAxisY);
            paint.drawLine(
                chart.scaleColor,
                chart.lineWidthChart,
                0,
                chart.size.cx - chart.rightVScaleWidth,
                hAxisY,
                chart.size.cx - chart.rightVScaleWidth + 8,
                hAxisY);
            String drawText = start.toStringAsFixed(chart.indDigit);
            FCSize tSize = paint.textSize(drawText, chart.font);
            paint.drawText(
                drawText,
                chart.textColor,
                chart.font,
                chart.size.cx - chart.rightVScaleWidth + 10,
                hAxisY - tSize.cy / 2);
            paint.drawText(drawText, chart.textColor, chart.font,
                chart.leftVScaleWidth - tSize.cx - 10, hAxisY - tSize.cy / 2);
          }
          start += chart.gridStepChart;
        }
      }
    }
  }
  if (paint.hasPaintChartHScaleEvent) {
    paint.onPaintChartHScale(chart, paint, clipRect);
  } else {
    if (chart.data.isNotEmpty && chart.hScaleHeight > 0) {
      double dLeft = chart.leftVScaleWidth + 10;
      for (int i = chart.firstVisibleIndex; i <= chart.lastVisibleIndex; i++) {
        double dateNum = chart.data[i].date;
        String xText = dateNum.toString();
        if (chart.hScaleFormat.isNotEmpty) {
          xText = getFormatDate(dateNum, chart.hScaleFormat);
        } else {
          if (chart.cycle == "day") {
            xText = getFormatDate(dateNum, "YYYY-mm-dd");
          } else if (chart.cycle == "minute") {
            xText = getFormatDate(dateNum, "YYYY-mm-dd HH:MM");
          } else if (chart.cycle == "trend") {
            xText = getFormatDate(dateNum, "HH:MM");
          } else if (chart.cycle == "second") {
            xText = getFormatDate(dateNum, "HH:MM:SS");
          } else if (chart.cycle == "tick") {
            xText = (chart.crossStopIndex + 1).toString();
          }
        }
        FCSize tSize = paint.textSize(xText, chart.font);
        double x = getChartX(chart, i);
        double dx = x - tSize.cx / 2;
        if (dx > dLeft && dx < chart.size.cx - chart.rightVScaleWidth - 10) {
          paint.drawLine(
              chart.scaleColor,
              chart.lineWidthChart,
              0,
              x,
              chart.size.cy - chart.hScaleHeight,
              x,
              chart.size.cy - chart.hScaleHeight + 8);
          paint.drawText(xText, chart.textColor, chart.font, dx,
              chart.size.cy - chart.hScaleHeight + 8 - tSize.cy / 2 + 7);
          i = i + (tSize.cx + chart.hScaleTextDistance) ~/ chart.hScalePixel + 1;
        }
      }
    }
  }
}

/*
* 绘制十字线
* chart:图表
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawChartCrossLine(FCChart chart, FCPaint paint, FCRect clipRect) {
  if (!chart.data.isNotEmpty) {
    return;
  }
  double candleDivHeight = getCandleDivHeight(chart);
  double volDivHeight = getVolDivHeight(chart);
  double indDivHeight = getIndDivHeight(chart);
  double indDivHeight2 = getIndDivHeight2(chart);
  int crossLineIndex = chart.crossStopIndex;
  if (crossLineIndex == -1 || !chart.showCrossLine) {
    crossLineIndex = chart.lastVisibleIndex;
  }
  if (volDivHeight > 0) {
    List<String> drawTitles = <String>[];
    List<String> drawColors = <String>[];
    drawTitles.add("VOL ".toString() +
        chart.data[crossLineIndex].volume.toStringAsFixed(chart.volDigit));
    drawColors.add(chart.textColor);
    if (chart.shapes.isNotEmpty) {
      for (int i = 0; i < chart.shapes.length; i++) {
        BaseShape shape = chart.shapes[i];
        if (shape.divIndex == 1) {
          if (shape.title.isNotEmpty) {
            if (shape.shapeType == "bar" && shape.style == "2color") {
              drawTitles.add(shape.title +
                  " ".toString() +
                  shape.datas[crossLineIndex].toStringAsFixed(chart.volDigit));
              drawColors.add(shape.color2);
            } else {
              if (shape.shapeType != "text") {
                drawTitles.add(shape.title +
                    " ".toString() +
                    shape.datas[crossLineIndex]
                        .toStringAsFixed(chart.volDigit));
                drawColors.add(shape.color);
                if (shape.datas2.isNotEmpty) {
                  drawTitles.add(shape.title2 +
                      " ".toString() +
                      shape.datas2[crossLineIndex]
                          .toStringAsFixed(chart.volDigit));
                  drawColors.add(shape.color2);
                }
              }
            }
          }
        }
      }
    }
    double iLeft = chart.leftVScaleWidth + 5;
    for (int i = 0; i < drawTitles.length; i++) {
      FCSize tSize = paint.textSize(drawTitles[i], chart.font);
      paint.drawText(
          drawTitles[i], drawColors[i], chart.font, iLeft, candleDivHeight + 5);
      iLeft += tSize.cx + 5;
    }
  }
  //上面显示数据  高开低收
  if (chart.cycle == "trend") {
    List<String> drawTitles = <String>[];
    List<String> drawColors = <String>[];
    drawTitles.add("CLOSE ".toString() +
        chart.data[crossLineIndex].close.toStringAsFixed(chart.candleDigit));
    drawColors.add(chart.textColor);
    if (chart.shapes.isNotEmpty) {
      for (int i = 0; i < chart.shapes.length; i++) {
        BaseShape shape = chart.shapes[i];
        if (shape.divIndex == 0) {
          if (shape.title.isNotEmpty) {
            if (shape.shapeType == "bar" && shape.style == "2color") {
              drawTitles.add(shape.title +
                  " ".toString() +
                  shape.datas[crossLineIndex].toStringAsFixed(chart.candleDigit));
              drawColors.add(shape.color2);
            } else {
              if (shape.shapeType != "text") {
                drawTitles.add(shape.title +
                    " ".toString() +
                    shape.datas[crossLineIndex]
                        .toStringAsFixed(chart.candleDigit));
                drawColors.add(shape.color);
                if (shape.datas2.isNotEmpty) {
                  drawTitles.add(shape.title2 +
                      " ".toString() +
                      shape.datas2[crossLineIndex]
                          .toStringAsFixed(chart.candleDigit));
                  drawColors.add(shape.color2);
                }
              }
            }
          }
        }
      }
    }
    double iLeft = chart.leftVScaleWidth + 5;
    for (int i = 0; i < drawTitles.length; i++) {
      FCSize tSize = paint.textSize(drawTitles[i], chart.font);
      paint.drawText(drawTitles[i], drawColors[i], chart.font, iLeft, 5);
      iLeft += tSize.cx + 5;
    }
  } else {
    List<String> drawTitles = <String>[];
    List<String> drawColors = <String>[];
    if (chart.mainIndicator == "MA") {
      if (chart.ma5.isNotEmpty) {
        drawTitles.add("MA5 ".toString() +
            chart.ma5[crossLineIndex].toStringAsFixed(chart.candleDigit));
      } else {
        drawTitles.add("MA5");
      }
      drawColors.add(chart.indicatorColors[0]);
      if (chart.ma10.isNotEmpty) {
        drawTitles.add("MA10 ".toString() +
            chart.ma10[crossLineIndex].toStringAsFixed(chart.candleDigit));
      } else {
        drawTitles.add("MA10");
      }
      drawColors.add(chart.indicatorColors[1]);
      if (chart.ma20.isNotEmpty) {
        drawTitles.add("MA20 ".toString() +
            chart.ma20[crossLineIndex].toStringAsFixed(chart.candleDigit));
      } else {
        drawTitles.add("MA20");
      }
      drawColors.add(chart.indicatorColors[2]);
      if (chart.ma30.isNotEmpty) {
        drawTitles.add("MA30 ".toString() +
            chart.ma30[crossLineIndex].toStringAsFixed(chart.candleDigit));
      } else {
        drawTitles.add("MA30");
      }
      drawColors.add(chart.indicatorColors[5]);
      if (chart.ma120.isNotEmpty) {
        drawTitles.add("MA120 ".toString() +
            chart.ma120[crossLineIndex].toStringAsFixed(chart.candleDigit));
      } else {
        drawTitles.add("MA120");
      }
      drawColors.add(chart.indicatorColors[4]);
      if (chart.ma250.isNotEmpty) {
        drawTitles.add("MA250 ".toString() +
            chart.ma250[crossLineIndex].toStringAsFixed(chart.candleDigit));
      } else {
        drawTitles.add("MA250");
      }
      drawColors.add(chart.indicatorColors[3]);
    } else if (chart.mainIndicator == "BOLL") {
      if (chart.bollMid.isNotEmpty) {
        drawTitles.add("MID ".toString() +
            chart.bollMid[crossLineIndex].toStringAsFixed(chart.candleDigit));
      } else {
        drawTitles.add("MID");
      }
      drawColors.add(chart.indicatorColors[0]);
      if (chart.bollUp.isNotEmpty) {
        drawTitles.add("UP ".toString() +
            chart.bollUp[crossLineIndex].toStringAsFixed(chart.candleDigit));
      } else {
        drawTitles.add("UP");
      }
      drawColors.add(chart.indicatorColors[1]);
      if (chart.bollDown.isNotEmpty) {
        drawTitles.add("LOW ".toString() +
            chart.bollDown[crossLineIndex].toStringAsFixed(chart.candleDigit));
      } else {
        drawTitles.add("LOW");
      }
      drawColors.add(chart.indicatorColors[2]);
    }
    if (chart.shapes.isNotEmpty) {
      for (int i = 0; i < chart.shapes.length; i++) {
        BaseShape shape = chart.shapes[i];
        if (shape.divIndex == 0) {
          if (shape.title.isNotEmpty) {
            if (shape.shapeType == "bar" && shape.style == "2color") {
              drawTitles.add(shape.title +
                  " ".toString() +
                  shape.datas[crossLineIndex].toStringAsFixed(chart.candleDigit));
              drawColors.add(shape.color2);
            } else {
              if (shape.shapeType != "text") {
                drawTitles.add(shape.title +
                    " ".toString() +
                    shape.datas[crossLineIndex]
                        .toStringAsFixed(chart.candleDigit));
                drawColors.add(shape.color);
                if (shape.datas2.isNotEmpty) {
                  drawTitles.add(shape.title2 +
                      " ".toString() +
                      shape.datas2[crossLineIndex]
                          .toStringAsFixed(chart.candleDigit));
                  drawColors.add(shape.color2);
                }
              }
            }
          }
        }
      }
    }
    double iLeft = chart.leftVScaleWidth + 5;
    for (int i = 0; i < drawTitles.length; i++) {
      FCSize tSize = paint.textSize(drawTitles[i], chart.font);
      paint.drawText(drawTitles[i], drawColors[i], chart.font, iLeft, 5);
      iLeft += tSize.cx + 5;
    }
  }
  if (indDivHeight > 0) {
    List<String> drawTitles = <String>[];
    List<String> drawColors = <String>[];
    if (chart.showIndicator == "MACD") {
      if (chart.alldifarr.isNotEmpty) {
        drawTitles.add("DIF ".toString() +
            chart.alldifarr[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("DIF");
      }
      drawColors.add(chart.indicatorColors[0]);
      if (chart.alldeaarr.isNotEmpty) {
        drawTitles.add("DEA ".toString() +
            chart.alldeaarr[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("DEA");
      }
      drawColors.add(chart.indicatorColors[1]);
      if (chart.allmacdarr.isNotEmpty) {
        drawTitles.add("MACD ".toString() +
            chart.allmacdarr[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("MACD");
      }
      drawColors.add(chart.indicatorColors[4]);
    } else if (chart.showIndicator == "KDJ") {
      if (chart.kdjK.isNotEmpty) {
        drawTitles.add("K ".toString() +
            chart.kdjK[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("K");
      }
      drawColors.add(chart.indicatorColors[0]);
      if (chart.kdjD.isNotEmpty) {
        drawTitles.add("D ".toString() +
            chart.kdjD[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("D");
      }
      drawColors.add(chart.indicatorColors[1]);
      if (chart.kdjJ.isNotEmpty) {
        drawTitles.add("J ".toString() +
            chart.kdjJ[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("J");
      }
      drawColors.add(chart.indicatorColors[2]);
    } else if (chart.showIndicator == "RSI") {
      if (chart.rsi1.isNotEmpty) {
        drawTitles.add("RSI6 ".toString() +
            chart.rsi1[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("RSI6");
      }
      drawColors.add(chart.indicatorColors[5]);
      if (chart.rsi2.isNotEmpty) {
        drawTitles.add("RSI12 ".toString() +
            chart.rsi2[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("RSI12");
      }
      drawColors.add(chart.indicatorColors[1]);
      if (chart.rsi3.isNotEmpty) {
        drawTitles.add("RSI24 ".toString() +
            chart.rsi3[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("RSI24");
      }
      drawColors.add(chart.indicatorColors[2]);
    } else if (chart.showIndicator == "BIAS") {
      if (chart.bias1.isNotEmpty) {
        drawTitles.add("BIAS6 ".toString() +
            chart.bias1[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("BIAS6");
      }
      drawColors.add(chart.indicatorColors[5]);
      if (chart.bias2.isNotEmpty) {
        drawTitles.add("BIAS12 ".toString() +
            chart.bias2[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("BIAS12");
      }
      drawColors.add(chart.indicatorColors[1]);
      if (chart.bias3.isNotEmpty) {
        drawTitles.add("BIAS24 ".toString() +
            chart.bias3[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("BIAS24");
      }
      drawColors.add(chart.indicatorColors[2]);
    } else if (chart.showIndicator == "ROC") {
      if (chart.roc.isNotEmpty) {
        drawTitles.add("ROC ".toString() +
            chart.roc[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("ROC");
      }
      drawColors.add(chart.indicatorColors[0]);
      if (chart.rocMa.isNotEmpty) {
        drawTitles.add("ROCMA ".toString() +
            chart.rocMa[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("ROCMA");
      }
      drawColors.add(chart.indicatorColors[1]);
    } else if (chart.showIndicator == "WR") {
      if (chart.wr1.isNotEmpty) {
        drawTitles.add("WR5 ".toString() +
            chart.wr1[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("WR5");
      }
      drawColors.add(chart.indicatorColors[0]);
      if (chart.wr2.isNotEmpty) {
        drawTitles.add("WR10 ".toString() +
            chart.wr2[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("WR10");
      }
      drawColors.add(chart.indicatorColors[1]);
    } else if (chart.showIndicator == "CCI") {
      if (chart.cci.isNotEmpty) {
        drawTitles.add("CCI ".toString() +
            chart.cci[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("CCI");
      }
      drawColors.add(chart.indicatorColors[0]);
    } else if (chart.showIndicator == "BBI") {
      if (chart.bbi.isNotEmpty) {
        drawTitles.add("BBI ".toString() +
            chart.bbi[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("BBI");
      }
      drawColors.add(chart.indicatorColors[0]);
    } else if (chart.showIndicator == "TRIX") {
      if (chart.trix.isNotEmpty) {
        drawTitles.add("TRIX ".toString() +
            chart.trix[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("TRIX");
      }
      drawColors.add(chart.indicatorColors[0]);
      if (chart.trixMa.isNotEmpty) {
        drawTitles.add("TRIXMA ".toString() +
            chart.trixMa[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("TRIXMA");
      }
      drawColors.add(chart.indicatorColors[1]);
    } else if (chart.showIndicator == "DMA") {
      if (chart.dma1.isNotEmpty) {
        drawTitles.add("MA10 ".toString() +
            chart.dma1[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("MA10");
      }
      drawColors.add(chart.indicatorColors[0]);
      if (chart.dma2.isNotEmpty) {
        drawTitles.add("MA50 ".toString() +
            chart.dma2[crossLineIndex].toStringAsFixed(chart.indDigit));
      } else {
        drawTitles.add("MA50");
      }
      drawColors.add(chart.indicatorColors[1]);
    }
    if (chart.shapes.isNotEmpty) {
      for (int i = 0; i < chart.shapes.length; i++) {
        BaseShape shape = chart.shapes[i];
        if (shape.divIndex == 2) {
          if (shape.title.isNotEmpty) {
            if (shape.shapeType == "bar" && shape.style == "2color") {
              drawTitles.add(shape.title +
                  " ".toString() +
                  shape.datas[crossLineIndex].toStringAsFixed(chart.indDigit));
              drawColors.add(shape.color2);
            } else {
              if (shape.shapeType != "text") {
                drawTitles.add(shape.title +
                    " ".toString() +
                    shape.datas[crossLineIndex]
                        .toStringAsFixed(chart.indDigit));
                drawColors.add(shape.color);
                if (shape.datas2.isNotEmpty) {
                  drawTitles.add(shape.title2 +
                      " ".toString() +
                      shape.datas2[crossLineIndex]
                          .toStringAsFixed(chart.indDigit));
                  drawColors.add(shape.color2);
                }
              }
            }
          }
        }
      }
    }
    double iLeft = chart.leftVScaleWidth + 5;
    for (int i = 0; i < drawTitles.length; i++) {
      FCSize tSize = paint.textSize(drawTitles[i], chart.font);
      paint.drawText(drawTitles[i], drawColors[i], chart.font, iLeft,
          candleDivHeight + volDivHeight + 5);
      iLeft += tSize.cx + 5;
    }
  }
  if (indDivHeight2 > 0) {
    List<String> drawTitles = <String>[];
    List<String> drawColors = <String>[];
    if (chart.shapes.isNotEmpty) {
      for (int i = 0; i < chart.shapes.length; i++) {
        BaseShape shape = chart.shapes[i];
        if (shape.divIndex == 3) {
          if (shape.title.isNotEmpty) {
            if (shape.shapeType == "bar" && shape.style == "2color") {
              drawTitles.add(shape.title +
                  " ".toString() +
                  shape.datas[crossLineIndex].toStringAsFixed(chart.indDigit2));
              drawColors.add(shape.color2);
            } else {
              if (shape.shapeType != "text") {
                drawTitles.add(shape.title +
                    " ".toString() +
                    shape.datas[crossLineIndex]
                        .toStringAsFixed(chart.indDigit2));
                drawColors.add(shape.color);
                if (shape.datas2.isNotEmpty) {
                  drawTitles.add(shape.title2 +
                      " ".toString() +
                      shape.datas2[crossLineIndex]
                          .toStringAsFixed(chart.indDigit2));
                  drawColors.add(shape.color2);
                }
              }
            }
          }
        }
      }
    }
    if (drawTitles.isNotEmpty) {
      double iLeft = chart.leftVScaleWidth + 5;
      for (int i = 0; i < drawTitles.length; i++) {
        FCSize tSize = paint.textSize(drawTitles[i], chart.font);
        paint.drawText(drawTitles[i], drawColors[i], chart.font, iLeft,
            candleDivHeight + volDivHeight + indDivHeight + 5 + tSize.cy / 2);
        iLeft += tSize.cx + 5;
      }
    }
  }

  if (chart.showCrossLine) {
    String rightText = "";
    if (chart.touchPosition.y < candleDivHeight) {
      rightText = getChartValue(chart, chart.touchPosition)
          .toStringAsFixed(chart.candleDigit);
    } else if (chart.touchPosition.y > candleDivHeight &&
        chart.touchPosition.y < candleDivHeight + volDivHeight) {
      rightText = getChartValue(chart, chart.touchPosition)
          .toStringAsFixed(chart.volDigit);
    } else if (chart.touchPosition.y > candleDivHeight + volDivHeight &&
        chart.touchPosition.y < candleDivHeight + volDivHeight + indDivHeight) {
      rightText = getChartValue(chart, chart.touchPosition)
          .toStringAsFixed(chart.indDigit);
    } else if (chart.touchPosition.y >
            candleDivHeight + volDivHeight + indDivHeight &&
        chart.touchPosition.y <
            candleDivHeight + volDivHeight + indDivHeight + indDivHeight2) {
      rightText = getChartValue(chart, chart.touchPosition)
          .toStringAsFixed(chart.indDigit2);
    }

    double drawY = chart.touchPosition.y;
    if (drawY > chart.size.cy - chart.hScaleHeight) {
      drawY = chart.size.cy - chart.hScaleHeight;
    }
    FCSize tSize = paint.textSize(rightText, chart.font);
    if (chart.leftVScaleWidth > 0) {
      paint.fillRect(
          chart.crossTipColor,
          chart.leftVScaleWidth - tSize.cx,
          drawY - tSize.cy / 2 - 4,
          chart.leftVScaleWidth,
          drawY + tSize.cy / 2 + 3);
      paint.drawText(rightText, chart.textColor, chart.font,
          chart.leftVScaleWidth - tSize.cx, drawY - tSize.cy / 2);
    }
    if (chart.rightVScaleWidth > 0) {
      paint.fillRect(
          chart.crossTipColor,
          chart.size.cx - chart.rightVScaleWidth,
          drawY - tSize.cy / 2 - 4,
          chart.size.cx - chart.rightVScaleWidth + tSize.cx,
          drawY + tSize.cy / 2 + 3);
      paint.drawText(rightText, chart.textColor, chart.font,
          chart.size.cx - chart.rightVScaleWidth, drawY - tSize.cy / 2);
    }
    //绘制十字线
    double drawX = getChartX(chart, chart.crossStopIndex);
    if(chart.getPaint().getTouchDownView() == chart){
      drawX = chart.touchPosition.x;
    }
    if (drawX < chart.leftVScaleWidth) {
      drawX = chart.leftVScaleWidth;
    }
    if (drawX > chart.size.cx - chart.rightVScaleWidth) {
      drawX = chart.size.cx - chart.rightVScaleWidth;
    }
    if (!chart.getSelectedPlot().isNotEmpty && chart.selectShape == "") {
      paint.drawLine(
          chart.crossLineColor,
          chart.lineWidthChart,
          0,
          chart.leftVScaleWidth,
          drawY,
          chart.size.cx - chart.rightVScaleWidth,
          drawY);
      paint.drawLine(chart.crossLineColor, chart.lineWidthChart, 0, drawX, 0,
          drawX, chart.size.cy - chart.hScaleHeight);
    }
    if (chart.crossStopIndex != -1) {
      double dateNum = chart.data[chart.crossStopIndex].date;
      String xText = dateNum.toString();
      if (chart.hScaleFormat.isNotEmpty) {
        xText = getFormatDate(dateNum, chart.hScaleFormat);
      } else {
        if (chart.cycle == "day") {
          xText = getFormatDate(dateNum, "YYYY-mm-dd");
        } else if (chart.cycle == "minute") {
          xText = getFormatDate(dateNum, "YYYY-mm-dd HH:MM");
        } else if (chart.cycle == "trend") {
          xText = getFormatDate(dateNum, "HH:MM");
        } else if (chart.cycle == "second") {
          xText = getFormatDate(dateNum, "HH:MM:SS");
        } else if (chart.cycle == "tick") {
          xText = (chart.crossStopIndex + 1).toString();
        }
      }
      FCSize xSize = paint.textSize(xText, chart.font);
      paint.fillRect(
          chart.crossTipColor,
          drawX - xSize.cx / 2 - 2,
          candleDivHeight + volDivHeight + indDivHeight,
          drawX + xSize.cx / 2 + 2,
          candleDivHeight + volDivHeight + indDivHeight + xSize.cy + 6);
      paint.drawText(xText, chart.textColor, chart.font, drawX - xSize.cx / 2,
          candleDivHeight + volDivHeight + indDivHeight + 3);
    }
  }
}

/*
* 计算最大最小值
* chart:图表
*/
void calculateChartMaxMin(FCChart chart) {
  chart.candleMax = 0;
  chart.candleMin = 0;
  chart.volMax = 0;
  chart.volMin = 0;
  chart.indMin = 0;
  chart.indMin = 0;
  bool load1 = false;
  bool load2 = false;
  bool load3 = false;
  bool load4 = false;
  bool isTrend = chart.cycle == "trend";
  double firstOpen = 0;
  if (chart.data.isNotEmpty) {
    int lastValidIndex = chart.lastVisibleIndex;
    if (chart.lastValidIndex != -1) {
      lastValidIndex = chart.lastValidIndex;
    }
    for (int i = chart.firstVisibleIndex; i <= lastValidIndex; i++) {
      if (i == chart.firstVisibleIndex) {
        if (isTrend) {
          chart.candleMax = chart.data[i].close;
          chart.candleMin = chart.data[i].close;
          firstOpen = chart.data[i].close;
        } else {
          chart.candleMax = chart.data[i].high;
          chart.candleMin = chart.data[i].low;
        }
        chart.volMax = chart.data[i].volume;
        load1 = true;
        load2 = true;
        if (chart.showIndicator == "MACD") {
          chart.indMax = chart.alldifarr[i];
          chart.indMin = chart.alldifarr[i];
          load3 = true;
        } else if (chart.showIndicator == "KDJ") {
          chart.indMax = chart.kdjK[i];
          chart.indMin = chart.kdjK[i];
          load3 = true;
        } else if (chart.showIndicator == "RSI") {
          chart.indMax = chart.rsi1[i];
          chart.indMin = chart.rsi1[i];
          load3 = true;
        } else if (chart.showIndicator == "BIAS") {
          chart.indMax = chart.bias1[i];
          chart.indMin = chart.bias1[i];
          load3 = true;
        } else if (chart.showIndicator == "ROC") {
          chart.indMax = chart.roc[i];
          chart.indMin = chart.roc[i];
          load3 = true;
        } else if (chart.showIndicator == "WR") {
          chart.indMax = chart.wr1[i];
          chart.indMin = chart.wr1[i];
          load3 = true;
        } else if (chart.showIndicator == "CCI") {
          chart.indMax = chart.cci[i];
          chart.indMin = chart.cci[i];
          load3 = true;
        } else if (chart.showIndicator == "BBI") {
          chart.indMax = chart.bbi[i];
          chart.indMin = chart.bbi[i];
          load3 = true;
        } else if (chart.showIndicator == "TRIX") {
          chart.indMax = chart.trix[i];
          chart.indMin = chart.trix[i];
          load3 = true;
        } else if (chart.showIndicator == "DMA") {
          chart.indMax = chart.dma1[i];
          chart.indMin = chart.dma1[i];
          load3 = true;
        }
      } else {
        if (isTrend) {
          if (chart.candleMax < chart.data[i].close) {
            chart.candleMax = chart.data[i].close;
          }
          if (chart.candleMin > chart.data[i].close) {
            chart.candleMin = chart.data[i].close;
          }
        } else {
          if (chart.candleMax < chart.data[i].high) {
            chart.candleMax = chart.data[i].high;
          }
          if (chart.candleMin > chart.data[i].low) {
            chart.candleMin = chart.data[i].low;
          }
        }
        if (chart.volMax < chart.data[i].volume) {
          chart.volMax = chart.data[i].volume;
        }
      }
      if (chart.showIndicator == "MACD") {
        if (chart.indMax < chart.alldifarr[i]) {
          chart.indMax = chart.alldifarr[i];
        }
        if (chart.indMax < chart.alldeaarr[i]) {
          chart.indMax = chart.alldeaarr[i];
        }
        if (chart.indMax < chart.allmacdarr[i]) {
          chart.indMax = chart.allmacdarr[i];
        }
        if (chart.indMin > chart.alldifarr[i]) {
          chart.indMin = chart.alldifarr[i];
        }
        if (chart.indMin > chart.alldeaarr[i]) {
          chart.indMin = chart.alldeaarr[i];
        }
        if (chart.indMin > chart.allmacdarr[i]) {
          chart.indMin = chart.allmacdarr[i];
        }
      } else if (chart.showIndicator == "KDJ") {
        if (chart.indMax < chart.kdjK[i]) {
          chart.indMax = chart.kdjK[i];
        }
        if (chart.indMax < chart.kdjD[i]) {
          chart.indMax = chart.kdjD[i];
        }
        if (chart.indMax < chart.kdjJ[i]) {
          chart.indMax = chart.kdjJ[i];
        }
        if (chart.indMin > chart.kdjK[i]) {
          chart.indMin = chart.kdjK[i];
        }
        if (chart.indMin > chart.kdjD[i]) {
          chart.indMin = chart.kdjD[i];
        }
        if (chart.indMin > chart.kdjJ[i]) {
          chart.indMin = chart.kdjJ[i];
        }
      } else if (chart.showIndicator == "RSI") {
        if (chart.indMax < chart.rsi1[i]) {
          chart.indMax = chart.rsi1[i];
        }
        if (chart.indMax < chart.rsi2[i]) {
          chart.indMax = chart.rsi2[i];
        }
        if (chart.indMax < chart.rsi3[i]) {
          chart.indMax = chart.rsi3[i];
        }
        if (chart.indMin > chart.rsi1[i]) {
          chart.indMin = chart.rsi1[i];
        }
        if (chart.indMin > chart.rsi2[i]) {
          chart.indMin = chart.rsi2[i];
        }
        if (chart.indMin > chart.rsi3[i]) {
          chart.indMin = chart.rsi3[i];
        }
      } else if (chart.showIndicator == "BIAS") {
        if (chart.indMax < chart.bias1[i]) {
          chart.indMax = chart.bias1[i];
        }
        if (chart.indMax < chart.bias2[i]) {
          chart.indMax = chart.bias2[i];
        }
        if (chart.indMax < chart.bias3[i]) {
          chart.indMax = chart.bias3[i];
        }
        if (chart.indMin > chart.bias1[i]) {
          chart.indMin = chart.bias1[i];
        }
        if (chart.indMin > chart.bias2[i]) {
          chart.indMin = chart.bias2[i];
        }
        if (chart.indMin > chart.bias3[i]) {
          chart.indMin = chart.bias3[i];
        }
      } else if (chart.showIndicator == "ROC") {
        if (chart.indMax < chart.roc[i]) {
          chart.indMax = chart.roc[i];
        }
        if (chart.indMax < chart.rocMa[i]) {
          chart.indMax = chart.rocMa[i];
        }
        if (chart.indMin > chart.roc[i]) {
          chart.indMin = chart.roc[i];
        }
        if (chart.indMin > chart.rocMa[i]) {
          chart.indMin = chart.rocMa[i];
        }
      } else if (chart.showIndicator == "WR") {
        if (chart.indMax < chart.wr1[i]) {
          chart.indMax = chart.wr1[i];
        }
        if (chart.indMax < chart.wr2[i]) {
          chart.indMax = chart.wr2[i];
        }
        if (chart.indMin > chart.wr1[i]) {
          chart.indMin = chart.wr1[i];
        }
        if (chart.indMin > chart.wr2[i]) {
          chart.indMin = chart.wr2[i];
        }
      } else if (chart.showIndicator == "CCI") {
        if (chart.indMax < chart.cci[i]) {
          chart.indMax = chart.cci[i];
        }
        if (chart.indMin > chart.cci[i]) {
          chart.indMin = chart.cci[i];
        }
      } else if (chart.showIndicator == "BBI") {
        if (chart.indMax < chart.bbi[i]) {
          chart.indMax = chart.bbi[i];
        }
        if (chart.indMin > chart.bbi[i]) {
          chart.indMin = chart.bbi[i];
        }
      } else if (chart.showIndicator == "TRIX") {
        if (chart.indMax < chart.trix[i]) {
          chart.indMax = chart.trix[i];
        }
        if (chart.indMax < chart.trixMa[i]) {
          chart.indMax = chart.trixMa[i];
        }
        if (chart.indMin > chart.trix[i]) {
          chart.indMin = chart.trix[i];
        }
        if (chart.indMin > chart.trixMa[i]) {
          chart.indMin = chart.trixMa[i];
        }
      } else if (chart.showIndicator == "DMA") {
        if (chart.indMax < chart.dma1[i]) {
          chart.indMax = chart.dma1[i];
        }
        if (chart.indMax < chart.dma2[i]) {
          chart.indMax = chart.dma2[i];
        }
        if (chart.indMin > chart.dma1[i]) {
          chart.indMin = chart.dma1[i];
        }
        if (chart.indMin > chart.dma2[i]) {
          chart.indMin = chart.dma2[i];
        }
      }
    }
  }
  if (chart.shapes.isNotEmpty) {
    int lastValidIndex = chart.lastVisibleIndex;
    if (chart.lastValidIndex != -1) {
      lastValidIndex = chart.lastValidIndex;
    }
    for (int s = 0; s < chart.shapes.length; s++) {
      BaseShape shape = chart.shapes[s];
      if (shape.datas.isNotEmpty) {
        for (int i = chart.firstVisibleIndex; i <= lastValidIndex; i++) {
          if (shape.divIndex == 0) {
            if (!load1 && i == chart.firstVisibleIndex) {
              if (shape.leftOrRight) {
                chart.candleMax = shape.datas[i];
                chart.candleMin = shape.datas[i];
              } else {
                chart.candleMaxRight = shape.datas[i];
                chart.candleMinRight = shape.datas[i];
              }
              load1 = true;
            } else {
              if (shape.leftOrRight) {
                if (shape.datas[i] > chart.candleMax) {
                  chart.candleMax = shape.datas[i];
                }
                if (shape.datas[i] < chart.candleMin) {
                  chart.candleMin = shape.datas[i];
                }
              } else {
                if (shape.datas[i] > chart.candleMaxRight) {
                  chart.candleMaxRight = shape.datas[i];
                }
                if (shape.datas[i] < chart.candleMinRight) {
                  chart.candleMinRight = shape.datas[i];
                }
              }
            }
          } else if (shape.divIndex == 1) {
            if (!load2 && i == chart.firstVisibleIndex) {
              if (shape.leftOrRight) {
                chart.volMax = shape.datas[i];
                chart.volMin = shape.datas[i];
              } else {
                chart.volMaxRight = shape.datas[i];
                chart.volMinRight = shape.datas[i];
              }
              load2 = true;
            } else {
              if (shape.leftOrRight) {
                if (shape.datas[i] > chart.volMax) {
                  chart.volMax = shape.datas[i];
                }
                if (shape.datas[i] < chart.volMin) {
                  chart.volMin = shape.datas[i];
                }
              } else {
                if (shape.datas[i] > chart.volMaxRight) {
                  chart.volMaxRight = shape.datas[i];
                }
                if (shape.datas[i] < chart.volMinRight) {
                  chart.volMinRight = shape.datas[i];
                }
              }
            }
          } else if (shape.divIndex == 2) {
            if (!load3 && i == chart.firstVisibleIndex) {
              if (shape.leftOrRight) {
                chart.indMax = shape.datas[i];
                chart.indMin = shape.datas[i];
              } else {
                chart.indMaxRight = shape.datas[i];
                chart.indMinRight = shape.datas[i];
              }
              load3 = true;
            } else {
              if (shape.leftOrRight) {
                if (shape.datas[i] > chart.indMax) {
                  chart.indMax = shape.datas[i];
                }
                if (shape.datas[i] < chart.indMin) {
                  chart.indMin = shape.datas[i];
                }
              } else {
                if (shape.datas[i] > chart.indMaxRight) {
                  chart.indMaxRight = shape.datas[i];
                }
                if (shape.datas[i] < chart.indMinRight) {
                  chart.indMinRight = shape.datas[i];
                }
              }
            }
          } else if (shape.divIndex == 3) {
            if (!load4 && i == chart.firstVisibleIndex) {
              if (shape.leftOrRight) {
                chart.indMax2 = shape.datas[i];
                chart.indMin2 = shape.datas[i];
              } else {
                chart.indMax2Right = shape.datas[i];
                chart.indMin2Right = shape.datas[i];
              }
              load4 = true;
            } else {
              if (shape.leftOrRight) {
                if (shape.datas[i] > chart.indMax2) {
                  chart.indMax2 = shape.datas[i];
                }
                if (shape.datas[i] < chart.indMin2) {
                  chart.indMin2 = shape.datas[i];
                }
              } else {
                if (shape.datas[i] > chart.indMax2Right) {
                  chart.indMax2Right = shape.datas[i];
                }
                if (shape.datas[i] < chart.indMin2Right) {
                  chart.indMin2Right = shape.datas[i];
                }
              }
            }
          }
        }
      }
      if (shape.datas2.isNotEmpty) {
        for (int i = chart.firstVisibleIndex; i <= lastValidIndex; i++) {
          if (shape.divIndex == 0) {
            if (shape.leftOrRight) {
              if (shape.datas2[i] > chart.candleMax) {
                chart.candleMax = shape.datas2[i];
              }
              if (shape.datas2[i] < chart.candleMin) {
                chart.candleMin = shape.datas2[i];
              }
            } else {
              if (shape.datas2[i] > chart.candleMaxRight) {
                chart.candleMaxRight = shape.datas2[i];
              }
              if (shape.datas2[i] < chart.candleMinRight) {
                chart.candleMinRight = shape.datas2[i];
              }
            }
          } else if (shape.divIndex == 1) {
            if (shape.leftOrRight) {
              if (shape.datas2[i] > chart.volMax) {
                chart.volMax = shape.datas2[i];
              }
              if (shape.datas2[i] < chart.volMin) {
                chart.volMin = shape.datas2[i];
              }
            } else {
              if (shape.datas2[i] > chart.volMaxRight) {
                chart.volMaxRight = shape.datas2[i];
              }
              if (shape.datas2[i] < chart.volMinRight) {
                chart.volMinRight = shape.datas2[i];
              }
            }
          } else if (shape.divIndex == 2) {
            if (shape.leftOrRight) {
              if (shape.datas2[i] > chart.indMax) {
                chart.indMax = shape.datas2[i];
              }
              if (shape.datas2[i] < chart.indMin) {
                chart.indMin = shape.datas2[i];
              }
            } else {
              if (shape.datas2[i] > chart.indMaxRight) {
                chart.indMaxRight = shape.datas2[i];
              }
              if (shape.datas2[i] < chart.indMinRight) {
                chart.indMinRight = shape.datas2[i];
              }
            }
          } else if (shape.divIndex == 3) {
            if (shape.leftOrRight) {
              if (shape.datas2[i] > chart.indMax2) {
                chart.indMax2 = shape.datas2[i];
              }
              if (shape.datas2[i] < chart.indMin2) {
                chart.indMin2 = shape.datas2[i];
              }
            } else {
              if (shape.datas2[i] > chart.indMax2Right) {
                chart.indMax2Right = shape.datas2[i];
              }
              if (shape.datas2[i] < chart.indMin2Right) {
                chart.indMin2Right = shape.datas2[i];
              }
            }
          }
        }
      }
    }
  }
  if (isTrend) {
    double subMax = max((chart.candleMax - firstOpen).abs(),
        (chart.candleMin - firstOpen).abs());
    chart.candleMax = firstOpen + subMax;
    chart.candleMin = firstOpen - subMax;
  } else {
    if (chart.candleMax == 0 && chart.candleMin == 0) {
      chart.candleMax = 1;
      chart.candleMin = -1;
    }
    if (chart.volMax == 0 && chart.volMin == 0) {
      chart.volMax = 1;
      chart.volMin = -1;
    }
    if (chart.indMax == 0 && chart.indMin == 0) {
      chart.indMax = 1;
      chart.indMin = -1;
    }
    if (chart.indMax2 == 0 && chart.indMin2 == 0) {
      chart.indMax2 = 1;
      chart.indMin2 = -1;
    }
    if (chart.candleMaxRight == 0 && chart.candleMinRight == 0) {
      chart.candleMaxRight = 1;
      chart.candleMinRight = -1;
    }
    if (chart.volMaxRight == 0 && chart.volMinRight == 0) {
      chart.volMaxRight = 1;
      chart.volMinRight = -1;
    }
    if (chart.indMaxRight == 0 && chart.indMinRight == 0) {
      chart.indMaxRight = 1;
      chart.indMinRight = -1;
    }
    if (chart.indMax2Right == 0 && chart.indMin2Right == 0) {
      chart.indMax2Right = 1;
      chart.indMin2Right = -1;
    }
  }
}

/*
* 绘制线条
* chart:图表
* paint:绘图对象
* clipRect:裁剪区域
* divIndex:图层
* datas:数据
* color:颜色
* selected:是否选中
*/
void drawChartLines(FCChart chart, FCPaint paint, FCRect clipRect, int divIndex,
    List<double> datas, String color, bool selected) {
  paint.beginPath();
  double lastX = 0, lastY = 0;
  int maxVisibleRecord = getChartMaxVisibleCount(
      chart, chart.hScalePixel, getChartWorkAreaWidth(chart));
  int lastValidIndex = chart.lastVisibleIndex;
  if (chart.lastValidIndex != -1) {
    lastValidIndex = chart.lastValidIndex;
  }
  for (int i = chart.firstVisibleIndex; i <= lastValidIndex; i++) {
    double x = getChartX(chart, i);
    double value = datas[i];
    double y = getChartY(chart, divIndex, value);
    if (i > chart.firstVisibleIndex) {
      paint.addLine(lastX, lastY, x, y);
    }
    lastX = x;
    lastY = y;
    if (selected) {
      int kPInterval = maxVisibleRecord ~/ 30;
      if (kPInterval < 2) {
        kPInterval = 3;
      }
      if (i % kPInterval == 0) {
        paint.fillRect(color, x - 3, y - 3, x + 3, y + 3);
      }
    }
  }
  paint.drawPath(color, chart.lineWidthChart, 0);
  paint.closePath();
}

/*
* 绘制线条到右轴
* chart:图表
* paint:绘图对象
* clipRect:裁剪区域
* divIndex:图层
* datas:数据
* color:颜色
* selected:是否选中
*/
void drawChartLinesInRight(FCChart chart, FCPaint paint, FCRect clipRect,
    int divIndex, List<double> datas, String color, bool selected) {
  paint.beginPath();
  double lastX = 0, lastY = 0;
  int maxVisibleRecord = getChartMaxVisibleCount(
      chart, chart.hScalePixel, getChartWorkAreaWidth(chart));
  int lastValidIndex = chart.lastVisibleIndex;
  if (chart.lastValidIndex != -1) {
    lastValidIndex = chart.lastValidIndex;
  }
  for (int i = chart.firstVisibleIndex; i <= lastValidIndex; i++) {
    double x = getChartX(chart, i);
    double value = datas[i];
    double y = getChartYInRight(chart, divIndex, value);
    if (i > chart.firstVisibleIndex) {
      paint.addLine(lastX, lastY, x, y);
    }
    lastX = x;
    lastY = y;
    if (selected) {
      int kPInterval = maxVisibleRecord ~/ 30;
      if (kPInterval < 2) {
        kPInterval = 3;
      }
      if (i % kPInterval == 0) {
        paint.fillRect(color, x - 3, y - 3, x + 3, y + 3);
      }
    }
  }
  paint.drawPath(color, chart.lineWidthChart, 0);
  paint.closePath();
}

/*
* 绘制画线工具
* chart:图表
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawChartPlot(FCChart chart, FCPaint paint, FCRect clipRect) {
  paint.save();
  double divHeight = getCandleDivHeight(chart);
  paint.setClip(chart.leftVScaleWidth, 20,
      chart.size.cx - chart.rightVScaleWidth, divHeight);
  for (int i = 0; i < chart.plots.length; i++) {
    FCPlot plot = chart.plots[i];
    int index1 = 0, index2 = 0, index3 = 0;
    double mpx1 = 0, mpy1 = 0, mpx2 = 0, mpy2 = 0, mpx3 = 0, mpy3 = 0;
    if (plot.plotType == "LRLine" ||
        plot.plotType == "LRChannel" ||
        plot.plotType == "LRBand") {
      List<double> list = <double>[];
      index1 = getChartIndexByDate(chart, plot.key1);
      index2 = getChartIndexByDate(chart, plot.key2);
      int minIndex = min(index1, index2);
      int maxIndex = max(index1, index2);
      for (int j = minIndex; j <= maxIndex; j++) {
        list.add(chart.data[j].close);
      }
      linearRegressionEquation(chart, list);
      plot.value1 = chart.bChart;
      plot.value2 = chart.kChart * (maxIndex - minIndex + 1) + chart.bChart;
    } else if (plot.plotType == "BoxLine" ||
        plot.plotType == "TironeLevels" ||
        plot.plotType == "QuadrantLines") {
      getCandleRange(chart, plot);
      double nHigh = chart.nHighChart, nLow = chart.nLowChart;
      index1 = getChartIndexByDate(chart, plot.key1);
      index2 = getChartIndexByDate(chart, plot.key2);
      plot.key1 = getChartDateByIndex(chart, min(index1, index2));
      plot.key2 = getChartDateByIndex(chart, max(index1, index2));
      plot.value1 = nHigh;
      plot.value2 = nLow;
    }
    if (plot.key1 > 0) {
      index1 = getChartIndexByDate(chart, plot.key1);
      mpx1 = getChartX(chart, index1);
      mpy1 = getChartY(chart, 0, plot.value1);
      if (chart.getSelectedPlot() == plot) {
        paint.fillEllipse(
            plot.pointColor,
            mpx1 - chart.plotPointSizeChart,
            mpy1 - chart.plotPointSizeChart,
            mpx1 + chart.plotPointSizeChart,
            mpy1 + chart.plotPointSizeChart);
      }
    }
    if (plot.key2 > 0) {
      index2 = getChartIndexByDate(chart, plot.key2);
      mpx2 = getChartX(chart, index2);
      mpy2 = getChartY(chart, 0, plot.value2);
      if (chart.getSelectedPlot() == plot) {
        paint.fillEllipse(
            plot.pointColor,
            mpx2 - chart.plotPointSizeChart,
            mpy2 - chart.plotPointSizeChart,
            mpx2 + chart.plotPointSizeChart,
            mpy2 + chart.plotPointSizeChart);
      }
    }
    if (plot.key3 > 0) {
      index3 = getChartIndexByDate(chart, plot.key3);
      mpx3 = getChartX(chart, index3);
      mpy3 = getChartY(chart, 0, plot.value3);
      if (chart.getSelectedPlot() == plot) {
        paint.fillEllipse(
            plot.pointColor,
            mpx3 - chart.plotPointSizeChart,
            mpy3 - chart.plotPointSizeChart,
            mpx3 + chart.plotPointSizeChart,
            mpy3 + chart.plotPointSizeChart);
      }
    }
    if (plot.plotType == "Line") {
      lineXY(chart, mpx1, mpy1, mpx2, mpy2, 0, 0);
      if (mpx2 == mpx1) {
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, mpx1, 0, mpx1, divHeight);
      } else {
        double newX1 = chart.leftVScaleWidth;
        double newY1 = newX1 * chart.kChart + chart.bChart;
        double newX2 = chart.size.cx - chart.rightVScaleWidth;
        double newY2 = newX2 * chart.kChart + chart.bChart;
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, newX1, newY1, newX2, newY2);
      }
    } else if (plot.plotType == "ArrowSegment") {
      int arrowSize = 24;
      double slopy = 0, cosy = 0, siny = 0;
      slopy = atan2(mpy1 - mpy2, mpx1 - mpx2);
      cosy = cos(slopy);
      siny = sin(slopy);
      FCPoint ptPoint = FCPoint(0, 0);
      ptPoint.x = mpx2;
      ptPoint.y = mpy2;
      List<FCPoint> pts = <FCPoint>[];
      pts.add(FCPoint(0, 0));
      pts.add(FCPoint(0, 0));
      pts.add(FCPoint(0, 0));
      pts[0] = ptPoint;
      pts[1].x =
          ptPoint.x + (arrowSize * cosy - (arrowSize / 2.0 * siny) + 0.5);
      pts[1].y =
          ptPoint.y + (arrowSize * siny + (arrowSize / 2.0 * cosy) + 0.5);
      pts[2].x = ptPoint.x + (arrowSize * cosy + arrowSize / 2.0 * siny + 0.5);
      pts[2].y = ptPoint.y - (arrowSize / 2.0 * cosy - arrowSize * siny + 0.5);
      arrowSize = 20;
      FCPoint ptPoint2 = FCPoint(0, 0);
      ptPoint2.x = mpx2;
      ptPoint2.y = mpy2;
      List<FCPoint> pts2 = <FCPoint>[];
      pts2.add(FCPoint(0, 0));
      pts2.add(FCPoint(0, 0));
      pts2.add(FCPoint(0, 0));
      pts2[0] = ptPoint2;
      pts2[1].x =
          ptPoint2.x + (arrowSize * cosy - (arrowSize / 2.0 * siny) + 0.5);
      pts2[1].y =
          ptPoint2.y + (arrowSize * siny + (arrowSize / 2.0 * cosy) + 0.5);
      pts2[2].x =
          ptPoint2.x + (arrowSize * cosy + arrowSize / 2.0 * siny + 0.5);
      pts2[2].y =
          ptPoint2.y - (arrowSize / 2.0 * cosy - arrowSize * siny + 0.5);
      lineXY(chart, pts2[1].x, pts2[1].y, pts2[2].x, pts2[2].y, 0, 0);
      double newX1 = 0, newY1 = 0, newX2 = 0, newY2 = 0;
      if (pts2[1].x > pts2[2].x) {
        newX1 = pts2[2].x + (pts2[1].x - pts2[2].x) / 3;
        newX2 = pts2[2].x + (pts2[1].x - pts2[2].x) * 2 / 3;
      } else {
        newX1 = pts2[1].x + (pts2[2].x - pts2[1].x) / 3;
        newX2 = pts2[1].x + (pts2[2].x - pts2[1].x) * 2 / 3;
      }
      if (chart.kChart == 0 && chart.bChart == 0) {
        if (pts2[1].y > pts2[2].y) {
          newY1 = pts2[2].y + (pts2[1].y - pts2[2].y) / 3;
          newY2 = pts2[2].y + (pts2[1].y - pts2[2].y) * 2 / 3;
        } else {
          newY1 = pts2[1].y + (pts2[2].y - pts2[1].y) / 3;
          newY2 = pts2[1].y + (pts2[2].y - pts2[1].y) * 2 / 3;
        }
      } else {
        newY1 = (chart.kChart * newX1) + chart.bChart;
        newY2 = (chart.kChart * newX2) + chart.bChart;
      }
      pts2[1].x = newX1;
      pts2[1].y = newY1;
      pts2[2].x = newX2;
      pts2[2].y = newY2;
      List<FCPoint> drawPoints = <FCPoint>[];
      drawPoints.add(FCPoint(0, 0));
      drawPoints.add(FCPoint(0, 0));
      drawPoints.add(FCPoint(0, 0));
      drawPoints.add(FCPoint(0, 0));
      drawPoints.add(FCPoint(0, 0));
      drawPoints.add(FCPoint(0, 0));
      drawPoints[0].x = ptPoint.x;
      drawPoints[0].y = ptPoint.y;
      drawPoints[1].x = pts[1].x;
      drawPoints[1].y = pts[1].y;
      if (mpy1 >= mpy2) {
        drawPoints[2].x = pts2[1].x;
        drawPoints[2].y = pts2[1].y;
      } else {
        drawPoints[2].x = pts2[2].x;
        drawPoints[2].y = pts2[2].y;
      }
      drawPoints[3].x = mpx1;
      drawPoints[3].y = mpy1;
      if (mpy1 >= mpy2) {
        drawPoints[4].x = pts2[2].x;
        drawPoints[4].y = pts2[2].y;
      } else {
        drawPoints[4].x = pts2[1].x;
        drawPoints[4].y = pts2[1].y;
      }
      drawPoints[5].x = pts[2].x;
      drawPoints[5].y = pts[2].y;

      paint.beginPath();
      for (int j = 0; j < 6; j++) {
        if (j > 0) {
          paint.addLine(drawPoints[j - 1].x, drawPoints[j - 1].y,
              drawPoints[j].x, drawPoints[j].y);
        }
      }
      paint.fillPath(plot.lineColor);
      paint.closePath();
    } else if (plot.plotType == "AngleLine") {
      lineXY(chart, mpx1, mpy1, mpx2, mpy2, 0, 0);
      if (mpx2 == mpx1) {
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, mpx1, 0, mpx1, divHeight);
      } else {
        double newX1 = chart.leftVScaleWidth;
        double newY1 = newX1 * chart.kChart + chart.bChart;
        double newX2 = chart.size.cx - chart.rightVScaleWidth;
        double newY2 = newX2 * chart.kChart + chart.bChart;
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, newX1, newY1, newX2, newY2);
      }
      lineXY(chart, mpx1, mpy1, mpx3, mpy3, 0, 0);
      if (mpx3 == mpx1) {
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, mpx1, 0, mpx1, divHeight);
      } else {
        double newX1 = chart.leftVScaleWidth;
        double newY1 = newX1 * chart.kChart + chart.bChart;
        double newX2 = chart.size.cx - chart.rightVScaleWidth;
        double newY2 = newX2 * chart.kChart + chart.bChart;
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, newX1, newY1, newX2, newY2);
      }
    } else if (plot.plotType == "Parallel") {
      lineXY(chart, mpx1, mpy1, mpx2, mpy2, 0, 0);
      if (mpx2 == mpx1) {
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, mpx1, 0, mpx1, divHeight);
      } else {
        double newX1 = chart.leftVScaleWidth;
        double newY1 = newX1 * chart.kChart + chart.bChart;
        double newX2 = chart.size.cx - chart.rightVScaleWidth;
        double newY2 = newX2 * chart.kChart + chart.bChart;
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, newX1, newY1, newX2, newY2);
      }
      double newB = mpy3 - chart.kChart * mpx3;
      if (mpx2 == mpx1) {
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, mpx3, 0, mpx3, divHeight);
      } else {
        double newX1 = chart.leftVScaleWidth;
        double newY1 = newX1 * chart.kChart + newB;
        double newX2 = chart.size.cx - chart.rightVScaleWidth;
        double newY2 = newX2 * chart.kChart + newB;
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, newX1, newY1, newX2, newY2);
      }
    } else if (plot.plotType == "Percent") {
      List<double> list = getPercentParams(mpy1, mpy2);
      List<String> texts = <String>[];
      texts.add("0%");
      texts.add("25%");
      texts.add("50%");
      texts.add("75%");
      texts.add("100%");
      for (int j = 0; j < list.length; j++) {
        paint.drawLine(plot.lineColor, plot.lineWidth, 0, chart.leftVScaleWidth,
            list[j], chart.size.cx - chart.rightVScaleWidth, list[j]);
        FCSize tSize = paint.textSize(texts[j], chart.font);
        paint.drawText(texts[j], chart.textColor, chart.font,
            chart.leftVScaleWidth + 5, list[j] - tSize.cy - 2);
      }
    } else if (plot.plotType == "FiboTimezone") {
      int fValue = 1;
      int aIndex = index1;
      int pos = 1;
      paint.drawLine(
          plot.lineColor, plot.lineWidth, 0, mpx1, 0, mpx1, divHeight);
      FCSize tSize = paint.textSize("1", chart.font);
      paint.drawText(
          "1", chart.textColor, chart.font, mpx1, divHeight - tSize.cy);
      while (aIndex + fValue <= chart.lastVisibleIndex) {
        fValue = fibonacciValue(pos);
        int newIndex = aIndex + fValue;
        double newX = getChartX(chart, newIndex);
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, newX, 0, newX, divHeight);
        FCSize tSize2 = paint.textSize(fValue.toString(), chart.font);
        paint.drawText(fValue.toString(), chart.textColor, chart.font, newX,
            divHeight - tSize2.cy);
        pos++;
      }
    } else if (plot.plotType == "SpeedResist") {
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, mpx2, mpy2);
      if (mpx1 != mpx2 && mpy1 != mpy2) {
        FCPoint firstP = FCPoint(mpx2, mpy2 - (mpy2 - mpy1) / 3);
        FCPoint secondP = FCPoint(mpx2, mpy2 - (mpy2 - mpy1) * 2 / 3);
        FCPoint startP = FCPoint(mpx1, mpy1);
        double fK = 0, fB = 0, sK = 0, sB = 0;
        lineXY(chart, startP.x, startP.y, firstP.x, firstP.y, 0, 0);
        fK = chart.kChart;
        fB = chart.bChart;
        lineXY(chart, startP.x, startP.y, secondP.x, secondP.y, 0, 0);
        sK = chart.kChart;
        sB = chart.bChart;
        double newYF = 0, newYS = 0;
        double newX = 0;
        if (mpx2 > mpx1) {
          newYF = fK * (chart.size.cx - chart.rightVScaleWidth) + fB;
          newYS = sK * (chart.size.cx - chart.rightVScaleWidth) + sB;
          newX = (chart.size.cx - chart.rightVScaleWidth);
        } else {
          newYF = fB;
          newYS = sB;
          newX = chart.leftVScaleWidth;
        }
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, startP.x, startP.y, newX, newYF);
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, startP.x, startP.y, newX, newYS);
      }
    } else if (plot.plotType == "FiboFanline") {
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, mpx2, mpy2);
      if (mpx1 != mpx2 && mpy1 != mpy2) {
        FCPoint firstP = FCPoint(mpx2, mpy2 - (mpy2 - mpy1) * 0.382);
        FCPoint secondP = FCPoint(mpx2, mpy2 - (mpy2 - mpy1) * 0.5);
        FCPoint thirdP = FCPoint(mpx2, mpy2 - (mpy2 - mpy1) * 0.618);
        FCPoint startP = FCPoint(mpx1, mpy1);
        List<FCPoint> listP = <FCPoint>[];
        listP.add(firstP);
        listP.add(secondP);
        listP.add(thirdP);
        int listSize = listP.length;
        for (int j = 0; j < listSize; j++) {
          //获取直线参数
          lineXY(chart, startP.x, startP.y, listP[j].x, listP[j].y, 0, 0);
          double newX = 0;
          double newY = 0;
          if (mpx2 > mpx1) {
            newY = chart.kChart * (chart.size.cx - chart.rightVScaleWidth) +
                chart.bChart;
            newX = (chart.size.cx - chart.rightVScaleWidth);
          } else {
            newY = chart.bChart;
            newX = chart.leftVScaleWidth;
          }
          paint.drawLine(plot.lineColor, plot.lineWidth, 0, startP.x, startP.y,
              newX, newY);
        }
      }
    } else if (plot.plotType == "LRLine") {
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, mpx2, mpy2);
    } else if (plot.plotType == "LRBand") {
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, mpx2, mpy2);
      getLRBandRange(chart, plot, chart.kChart, chart.bChart);
      mpy1 = getChartY(chart, 0, plot.value1 + chart.upSubValue);
      mpy2 = getChartY(chart, 0, plot.value2 + chart.upSubValue);
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, mpx2, mpy2);
      mpy1 = getChartY(chart, 0, plot.value1 - chart.downSubValue);
      mpy2 = getChartY(chart, 0, plot.value2 - chart.downSubValue);
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, mpx2, mpy2);
    } else if (plot.plotType == "LRChannel") {
      getLRBandRange(chart, plot, chart.kChart, chart.bChart);
      lineXY(chart, mpx1, mpy1, mpx2, mpy2, 0, 0);
      double rightX = chart.size.cx - chart.rightVScaleWidth;
      double rightY = rightX * chart.kChart + chart.bChart;
      paint.drawLine(
          plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, rightX, rightY);
      mpy1 = getChartY(chart, 0, plot.value1 + chart.upSubValue);
      mpy2 = getChartY(chart, 0, plot.value2 + chart.upSubValue);
      lineXY(chart, mpx1, mpy1, mpx2, mpy2, 0, 0);
      rightY = rightX * chart.kChart + chart.bChart;
      paint.drawLine(
          plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, rightX, rightY);
      mpy1 = getChartY(chart, 0, plot.value1 - chart.downSubValue);
      mpy2 = getChartY(chart, 0, plot.value2 - chart.downSubValue);
      lineXY(chart, mpx1, mpy1, mpx2, mpy2, 0, 0);
      rightY = rightX * chart.kChart + chart.bChart;
      paint.drawLine(
          plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, rightX, rightY);
    } else if (plot.plotType == "Segment") {
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, mpx2, mpy2);
    } else if (plot.plotType == "Ray") {
      lineXY(chart, mpx1, mpy1, mpx2, mpy2, 0, 0);
      if (chart.kChart != 0 || chart.bChart != 0) {
        double leftX = chart.leftVScaleWidth;
        double leftY = leftX * chart.kChart + chart.bChart;
        double rightX = chart.size.cx - chart.rightVScaleWidth;
        double rightY = rightX * chart.kChart + chart.bChart;
        if (mpx1 >= mpx2) {
          paint.drawLine(
              plot.lineColor, plot.lineWidth, 0, leftX, leftY, mpx1, mpy1);
        } else {
          paint.drawLine(
              plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, rightX, rightY);
        }
      }
      //垂直时
      else {
        if (mpy1 >= mpy2) {
          paint.drawLine(
              plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, mpx1, 0);
        } else {
          paint.drawLine(
              plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, mpx1, divHeight);
        }
      }
    } else if (plot.plotType == "Triangle") {
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, mpx2, mpy2);
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, mpx2, mpy2, mpx3, mpy3);
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, mpx3, mpy3);
    } else if (plot.plotType == "SymmetricTriangle") {
      if (mpx2 != mpx1) {
        double a = (mpy2 - mpy1) / (mpx2 - mpx1);
        double b = mpy1 - a * mpx1;
        double c = -a;
        double d = mpy3 - c * mpx3;
        double leftX = chart.leftVScaleWidth;
        double leftY = leftX * a + b;
        double rightX = chart.size.cx - chart.rightVScaleWidth;
        double rightY = rightX * a + b;
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, leftX, leftY, rightX, rightY);
        leftY = leftX * c + d;
        rightY = rightX * c + d;
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, leftX, leftY, rightX, rightY);
      } else {
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, mpx1, 0, mpx1, divHeight);
        paint.drawLine(
            plot.lineColor, plot.lineWidth, 0, mpx3, 0, mpx3, divHeight);
      }
    } else if (plot.plotType == "Rect") {
      double sX1 = min(mpx1, mpx2);
      double sY1 = min(mpy1, mpy2);
      double sX2 = max(mpx1, mpx2);
      double sY2 = max(mpy1, mpy2);
      paint.drawRect(plot.lineColor, plot.lineWidth, 0, sX1, sY1, sX2, sY2);
    } else if (plot.plotType == "Cycle") {
      double r = sqrt(
          ((mpx2 - mpx1) * (mpx2 - mpx1) + (mpy2 - mpy1) * (mpy2 - mpy1))
              .abs());
      paint.drawEllipse(plot.lineColor, plot.lineWidth, 0, mpx1 - r, mpy1 - r,
          mpx1 + r, mpy1 + r);
    } else if (plot.plotType == "CircumCycle") {
      ellipseOR(chart, mpx1, mpy1, mpx2, mpy2, mpx3, mpy3);
      paint.drawEllipse(
          plot.lineColor,
          plot.lineWidth,
          0,
          chart.oXChart - chart.rChart,
          chart.oYChart - chart.rChart,
          chart.oXChart + chart.rChart,
          chart.oYChart + chart.rChart);
    } else if (plot.plotType == "Ellipse") {
      double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
      if (mpx1 <= mpx2) {
        x1 = mpx2;
        y1 = mpy2;
        x2 = mpx1;
        y2 = mpy1;
      } else {
        x1 = mpx1;
        y1 = mpy1;
        x2 = mpx2;
        y2 = mpy2;
      }
      double x = x1 - (x1 - x2);
      double y = 0;
      double width = (x1 - x2) * 2;
      double height = 0;
      if (y1 >= y2) {
        height = (y1 - y2) * 2;
      } else {
        height = (y2 - y1) * 2;
      }
      y = y2 - height ~/ 2;
      paint.drawEllipse(
          plot.lineColor, plot.lineWidth, 0, x, y, x + width, y + height);
    } else if (plot.plotType == "ParalleGram") {
      parallelogram(chart, mpx1, mpy1, mpx2, mpy2, mpx3, mpy3);
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, mpx1, mpy1, mpx2, mpy2);
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, mpx2, mpy2, mpx3, mpy3);
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, mpx3, mpy3,
          chart.x4Chart, chart.y4Chart);
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, chart.x4Chart,
          chart.y4Chart, mpx1, mpy1);
    } else if (plot.plotType == "BoxLine") {
      double sX1 = min(mpx1, mpx2);
      double sX2 = max(mpx1, mpx2);
      double sY1 = min(mpy1, mpy2);
      double sY2 = max(mpy1, mpy2);
      paint.drawRect(plot.lineColor, plot.lineWidth, 0, sX1, sY1, sX2, sY2);
      paint.drawText(((index2 - index1).abs() + 1).toString(), chart.textColor,
          chart.font, sX1 + 2, sY1 + 2);
      List<double> closeList = <double>[];
      for (int j = index1; j <= index2; j++) {
        closeList.add(chart.data[j].close);
      }
      double avgClose = avgValue(closeList);
      double closeY = getChartY(chart, 0, avgClose);
      paint.drawLine(
          plot.lineColor, plot.lineWidth, 0, sX1, closeY, sX2, closeY);
      String drawAvg = avgClose.toStringAsFixed(chart.candleDigit);
      FCSize tSize = paint.textSize(drawAvg, chart.font);
      paint.drawText(
          drawAvg, chart.textColor, chart.font, sX1 + 2, closeY - tSize.cy - 2);
    } else if (plot.plotType == "TironeLevels") {
      double sX1 = min(mpx1, mpx2);
      double sY1 = min(mpy1, mpy2);
      double sX2 = max(mpx1, mpx2);
      double sY2 = max(mpy1, mpy2);
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, sX1, sY1, sX2, sY1);
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, sX1, sY2, sX2, sY2);
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, sX1 + (sX2 - sX1) / 2,
          sY1, sX1 + (sX2 - sX1) / 2, sY2);
      double t2 = chart.nHighChart - (chart.nHighChart - chart.nLowChart) / 3,
          t3 = chart.nHighChart - (chart.nHighChart - chart.nLowChart) / 2,
          t4 = chart.nHighChart - 2 * (chart.nHighChart - chart.nLowChart) / 3;
      List<double> tList = <double>[];
      tList.add(t2);
      tList.add(t3);
      tList.add(t4);
      for (int j = 0; j < tList.length; j++) {
        double y = getChartY(chart, 0, tList[j]);
        //画直线
        paint.drawLine(plot.lineColor, plot.lineWidth, 0, chart.leftVScaleWidth,
            y, chart.size.cx - chart.rightVScaleWidth, y);
        String str = tList[j].toStringAsFixed(chart.candleDigit);
        FCSize tSize = paint.textSize(str, chart.font);
        paint.drawText(str, chart.textColor, chart.font,
            chart.leftVScaleWidth + 2, y - tSize.cy - 2);
      }
    } else if (plot.plotType == "QuadrantLines") {
      double sX1 = min(mpx1, mpx2);
      double sY1 = min(mpy1, mpy2);
      double sX2 = max(mpx1, mpx2);
      double sY2 = max(mpy1, mpy2);
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, sX1, sY1, sX2, sY1);
      paint.drawLine(plot.lineColor, plot.lineWidth, 0, sX1, sY2, sX2, sY2);
      double t2 = chart.nHighChart - (chart.nHighChart - chart.nLowChart) / 4,
          t3 = chart.nHighChart - (chart.nHighChart - chart.nLowChart) / 2,
          t4 = chart.nHighChart - 3 * (chart.nHighChart - chart.nLowChart) / 4;
      List<double> tList = <double>[];
      tList.add(t2);
      tList.add(t3);
      tList.add(t4);
      for (int j = 0; j < tList.length; j++) {
        double y = getChartY(chart, 0, tList[j]);
        //画直线
        paint.drawLine(plot.lineColor, plot.lineWidth, 0, sX1, y, sX2, y);
      }
    } else if (plot.plotType == "GoldenRatio") {
      double sY1 = min(mpy1, mpy2);
      double sY2 = max(mpy1, mpy2);
      List<double> ranges = <double>[];
      ranges.add(0);
      ranges.add(0.236);
      ranges.add(0.382);
      ranges.add(0.5);
      ranges.add(0.618);
      ranges.add(0.809);
      ranges.add(1);
      ranges.add(1.382);
      ranges.add(1.618);
      ranges.add(2);
      ranges.add(2.382);
      ranges.add(2.618);
      for (int j = 0; j < ranges.length; j++) {
        double newY = sY1 <= sY2
            ? sY1 + (sY2 - sY1) * ranges[j]
            : sY2 + (sY1 - sY2) * (1 - ranges[j]);
        paint.drawLine(plot.lineColor, plot.lineWidth, 0, chart.leftVScaleWidth,
            newY, chart.size.cx - chart.rightVScaleWidth, newY);
        FCPoint newPoint = FCPoint(0, newY);
        double value = getCandleDivValue(chart, newPoint);
        String str = value.toStringAsFixed(chart.candleDigit);
        FCSize tSize = paint.textSize(str, chart.font);
        paint.drawText(str, chart.textColor, chart.font,
            chart.leftVScaleWidth + 2, newY - tSize.cy - 2);
      }
    }
  }
  paint.restore();
}

/*
* 绘制图表
* chart:图表
* paint:绘图对象
* clipRect:裁剪区域
*/
void drawChartStock(FCChart chart, FCPaint paint, FCRect clipRect) {
  double candleHeight = getCandleDivHeight(chart);
  double volHeight = getVolDivHeight(chart);
  double indHeight = getIndDivHeight(chart);
  bool isTrend = chart.cycle == "trend";
  int cWidth = (chart.hScalePixel - 3) ~/ 2;
  if (cWidth < 0) {
    cWidth = 0;
  }
  int lastValidIndex = chart.lastVisibleIndex;
  if (chart.lastValidIndex != -1) {
    lastValidIndex = chart.lastValidIndex;
  }
  if (chart.data.isNotEmpty) {
    int maxVisibleRecord = getChartMaxVisibleCount(
        chart, chart.hScalePixel, getChartWorkAreaWidth(chart));
    paint.save();
    paint.setClip(chart.leftVScaleWidth, 0,
        chart.size.cx - chart.rightVScaleWidth, candleHeight);
    if (isTrend) {
      paint.beginPath();
      double lastX = 0, lastY = 0;
      for (int i = chart.firstVisibleIndex; i <= lastValidIndex; i++) {
        double x = getChartX(chart, i);
        double close = chart.data[i].close;
        double closeY = getChartY(chart, 0, close);
        if (i > chart.firstVisibleIndex) {
          paint.addLine(lastX, lastY, x, closeY);
        }
        lastX = x;
        lastY = closeY;
      }
      paint.drawPath(chart.indicatorColors[7], chart.lineWidthChart, 0);
      paint.closePath();
    }
    bool hasMinTag = false, hasMaxTag = false;
    for (int i = chart.firstVisibleIndex; i <= lastValidIndex; i++) {
      double x = getChartX(chart, i);
      double open = chart.data[i].open;
      double close = chart.data[i].close;
      double high = chart.data[i].high;
      double low = chart.data[i].low;
      double openY = getChartY(chart, 0, open);
      double closeY = getChartY(chart, 0, close);
      double highY = getChartY(chart, 0, high);
      double lowY = getChartY(chart, 0, low);
      if (close >= open) {
        if (isTrend) {
        } else {
          paint.drawLine(
              chart.upColor, chart.lineWidthChart, 0, x, highY, x, lowY);
          if (cWidth > 0) {
            if (close == open) {
              paint.drawLine(chart.upColor, chart.lineWidthChart, 0, x - cWidth,
                  closeY, x + cWidth, closeY);
            } else {
              paint.fillRect(
                  chart.upColor, x - cWidth, closeY, x + cWidth, openY);
            }
          }
        }
      } else {
        if (isTrend) {
        } else {
          paint.drawLine(
              chart.downColor, chart.lineWidthChart, 0, x, highY, x, lowY);
          if (cWidth > 0) {
            paint.fillRect(
                chart.downColor, x - cWidth, openY, x + cWidth, closeY);
          }
        }
      }
      if (chart.selectShape == "CANDLE") {
        int kPInterval = maxVisibleRecord ~/ 30;
        if (kPInterval < 2) {
          kPInterval = 3;
        }
        if (i % kPInterval == 0) {
          if (isTrend) {
          } else {
            paint.fillRect(
                chart.indicatorColors[0], x - 3, closeY - 3, x + 3, closeY + 3);
          }
        }
      }
      if (!isTrend) {
        if (!hasMaxTag) {
          if (high == chart.candleMax) {
            String tag = high.toStringAsFixed(chart.candleDigit);
            FCSize tSize = paint.textSize(tag, chart.font);
            paint.drawText(tag, chart.textColor, chart.font, x - tSize.cx / 2,
                highY - tSize.cy / 2 - 2);
            hasMaxTag = true;
          }
        }
        if (!hasMinTag) {
          if (low == chart.candleMin) {
            String tag = low.toStringAsFixed(chart.candleDigit);
            FCSize tSize = paint.textSize(tag, chart.font);
            paint.drawText(tag, chart.textColor, chart.font, x - tSize.cx / 2,
                lowY + 2 + tSize.cy / 2);
            hasMinTag = true;
          }
        }
      }
    }
    paint.restore();
    for (int i = chart.firstVisibleIndex; i <= lastValidIndex; i++) {
      double x = getChartX(chart, i);
      double open = chart.data[i].open;
      double close = chart.data[i].close;
      double volY = 0;
      double zeroY = 0;
      if (volHeight > 0) {
        double volume = chart.data[i].volume;
        volY = getChartY(chart, 1, volume);
        zeroY = getChartY(chart, 1, 0);
      }
      if (close >= open) {
        if (isTrend) {
          if (volHeight > 0) {
            paint.drawLine(chart.indicatorColors[6], chart.lineWidthChart, 0, x,
                volY, x, zeroY);
          }
        } else {
          if (cWidth > 0) {
            if (volHeight > 0) {
              paint.fillRect(
                  chart.upColor, x - cWidth, volY, x + cWidth, zeroY);
            }
          } else {
            if (volHeight > 0) {
              paint.drawLine(chart.upColor, chart.lineWidthChart, 0, x - cWidth,
                  volY, x + cWidth, zeroY);
            }
          }
        }
      } else {
        if (isTrend) {
          if (volHeight > 0) {
            paint.drawLine(chart.indicatorColors[6], chart.lineWidthChart, 0, x,
                volY, x, zeroY);
          }
        } else {
          if (cWidth > 0) {
            if (volHeight > 0) {
              paint.fillRect(
                  chart.downColor, x - cWidth, volY, x + cWidth, zeroY);
            }
          } else {
            if (volHeight > 0) {
              paint.drawLine(chart.downColor, chart.lineWidthChart, 0,
                  x - cWidth, volY, x + cWidth, zeroY);
            }
          }
        }
      }
      if (chart.selectShape == "VOL") {
        int kPInterval = maxVisibleRecord ~/ 30;
        if (kPInterval < 2) {
          kPInterval = 3;
        }
        if (i % kPInterval == 0) {
          paint.fillRect(
              chart.indicatorColors[0], x - 3, volY - 3, x + 3, volY + 3);
        }
      }
    }
    if (!isTrend) {
      paint.save();
      paint.setClip(chart.leftVScaleWidth, 20,
          chart.size.cx - chart.rightVScaleWidth, candleHeight);
      if (chart.mainIndicator == "BOLL") {
        drawChartLines(
            chart,
            paint,
            clipRect,
            0,
            chart.bollMid,
            chart.indicatorColors[0],
            (chart.selectShape == chart.mainIndicator &&
                    chart.selectShapeEx == "MID")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            0,
            chart.bollUp,
            chart.indicatorColors[1],
            (chart.selectShape == chart.mainIndicator &&
                    chart.selectShapeEx == "UP")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            0,
            chart.bollDown,
            chart.indicatorColors[2],
            (chart.selectShape == chart.mainIndicator &&
                    chart.selectShapeEx == "DOWN")
                ? true
                : false);
      } else if (chart.mainIndicator == "MA") {
        drawChartLines(
            chart,
            paint,
            clipRect,
            0,
            chart.ma5,
            chart.indicatorColors[0],
            (chart.selectShape == chart.mainIndicator &&
                    chart.selectShapeEx == "5")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            0,
            chart.ma10,
            chart.indicatorColors[1],
            (chart.selectShape == chart.mainIndicator &&
                    chart.selectShapeEx == "10")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            0,
            chart.ma20,
            chart.indicatorColors[2],
            (chart.selectShape == chart.mainIndicator &&
                    chart.selectShapeEx == "20")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            0,
            chart.ma30,
            chart.indicatorColors[5],
            (chart.selectShape == chart.mainIndicator &&
                    chart.selectShapeEx == "30")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            0,
            chart.ma120,
            chart.indicatorColors[4],
            (chart.selectShape == chart.mainIndicator &&
                    chart.selectShapeEx == "120")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            0,
            chart.ma250,
            chart.indicatorColors[3],
            (chart.selectShape == chart.mainIndicator &&
                    chart.selectShapeEx == "250")
                ? true
                : false);
      }
      paint.restore();
    }
    if (indHeight > 0) {
      if (chart.showIndicator == "MACD") {
        double zeroY = getChartY(chart, 2, 0);
        paint.drawLine(
            chart.indicatorColors[4],
            chart.lineWidthChart,
            0,
            chart.leftVScaleWidth,
            zeroY,
            getChartX(chart, chart.lastVisibleIndex),
            zeroY);
        for (int i = chart.firstVisibleIndex; i <= lastValidIndex; i++) {
          double x = getChartX(chart, i);
          double macd = chart.allmacdarr[i];
          double macdY = getChartY(chart, 2, macd);
          if (macdY < zeroY) {
            paint.drawLine(chart.indicatorColors[3], chart.lineWidthChart, 0, x,
                macdY, x, zeroY);
          } else {
            paint.drawLine(chart.indicatorColors[4], chart.lineWidthChart, 0, x,
                macdY, x, zeroY);
          }
          if (chart.selectShape == chart.showIndicator &&
              chart.selectShapeEx == "MACD") {
            int kPInterval = maxVisibleRecord ~/ 30;
            if (kPInterval < 2) {
              kPInterval = 3;
            }
            if (i % kPInterval == 0) {
              paint.fillRect(
                  chart.indicatorColors[4], x - 3, macdY - 3, x + 3, macdY + 3);
            }
          }
        }
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.alldifarr,
            chart.indicatorColors[0],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "DIF")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.alldeaarr,
            chart.indicatorColors[1],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "DEA")
                ? true
                : false);
      } else if (chart.showIndicator == "KDJ") {
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.kdjK,
            chart.indicatorColors[0],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "K")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.kdjD,
            chart.indicatorColors[1],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "D")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.kdjJ,
            chart.indicatorColors[2],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "J")
                ? true
                : false);
      } else if (chart.showIndicator == "RSI") {
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.rsi1,
            chart.indicatorColors[5],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "6")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.rsi2,
            chart.indicatorColors[1],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "12")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.rsi3,
            chart.indicatorColors[2],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "24")
                ? true
                : false);
      } else if (chart.showIndicator == "BIAS") {
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.bias1,
            chart.indicatorColors[5],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "1")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.bias2,
            chart.indicatorColors[1],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "2")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.bias3,
            chart.indicatorColors[2],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "3")
                ? true
                : false);
      } else if (chart.showIndicator == "ROC") {
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.roc,
            chart.indicatorColors[0],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "ROC")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.rocMa,
            chart.indicatorColors[1],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "ROCMA")
                ? true
                : false);
      } else if (chart.showIndicator == "WR") {
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.wr1,
            chart.indicatorColors[0],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "1")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.wr2,
            chart.indicatorColors[1],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "2")
                ? true
                : false);
      } else if (chart.showIndicator == "CCI") {
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.cci,
            chart.indicatorColors[0],
            (chart.selectShape == chart.showIndicator) ? true : false);
      } else if (chart.showIndicator == "BBI") {
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.bbi,
            chart.indicatorColors[0],
            (chart.selectShape == chart.showIndicator) ? true : false);
      } else if (chart.showIndicator == "TRIX") {
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.trix,
            chart.indicatorColors[0],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "TRIX")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.trixMa,
            chart.indicatorColors[1],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "TRIXMA")
                ? true
                : false);
      } else if (chart.showIndicator == "DMA") {
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.dma1,
            chart.indicatorColors[0],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "DIF")
                ? true
                : false);
        drawChartLines(
            chart,
            paint,
            clipRect,
            2,
            chart.dma2,
            chart.indicatorColors[1],
            (chart.selectShape == chart.showIndicator &&
                    chart.selectShapeEx == "DIFMA")
                ? true
                : false);
      }
    }
  }
  //绘制扩展线条
  if (chart.shapes.isNotEmpty) {
    for (int i = 0; i < chart.shapes.length; i++) {
      BaseShape shape = chart.shapes[i];
      if (shape.shapeType == "bar") {
        for (int j = chart.firstVisibleIndex; j <= lastValidIndex; j++) {
          if (shape.showHideDatas.length > j &&
              shape.showHideDatas[j].toString() == "0") {
            continue;
          }
          double x = getChartX(chart, j);
          double y1 = 0;
          if (shape.leftOrRight) {
            y1 = getChartY(chart, shape.divIndex, shape.datas[j]);
          } else {
            y1 = getChartYInRight(chart, shape.divIndex, shape.datas[j]);
          }
          if (shape.style != "2color") {
            double y2 = 0;
            if (shape.leftOrRight) {
              y2 = getChartY(chart, shape.divIndex, shape.datas2[j]);
            } else {
              y2 = getChartYInRight(chart, shape.divIndex, shape.datas2[j]);
            }
            if (y1 >= y2) {
              paint.fillRect(shape.color, x - cWidth, y2, x + cWidth, y1);
            } else {
              paint.fillRect(shape.color, x - cWidth, y1, x + cWidth, y2);
            }
          } else {
            double y2 = 0;
            if (shape.leftOrRight) {
              y2 = getChartY(chart, shape.divIndex, 0);
            } else {
              y2 = getChartYInRight(chart, shape.divIndex, 0);
            }
            if (y1 >= y2) {
              paint.drawLine(shape.color2, 1, 0, x, y1, x, y2);
            } else {
              paint.drawLine(shape.color, 1, 0, x, y1, x, y2);
            }
            if (j == lastValidIndex) {
              paint.drawLine(shape.color2, 1, 0, chart.leftVScaleWidth, y2,
                  chart.size.cx - chart.rightVScaleWidth, y2);
            }
          }
        }
      } else if (shape.shapeType == "text") {
        for (int j = chart.firstVisibleIndex; j <= lastValidIndex; j++) {
          double x = getChartX(chart, j);
          if (shape.datas[j] != 0) {
            double y1 = 0;
            if (shape.leftOrRight) {
              y1 = getChartY(chart, shape.divIndex, shape.value);
            } else {
              y1 = getChartYInRight(chart, shape.divIndex, shape.value);
            }
            String drawText = shape.text;
            FCSize tSize = paint.textSize(drawText, "Default,14");
            paint.drawText(drawText, shape.color, "Default,14",
                x - tSize.cx / 2, y1 - tSize.cy / 2);
          }
        }
      } else {
        if (shape.leftOrRight) {
          drawChartLines(
              chart,
              paint,
              clipRect,
              shape.divIndex,
              shape.datas,
              shape.color,
              (chart.selectShape == shape.shapeName) ? true : false);
        } else {
          drawChartLinesInRight(
              chart,
              paint,
              clipRect,
              shape.divIndex,
              shape.datas,
              shape.color,
              (chart.selectShape == shape.shapeName) ? true : false);
        }
      }
    }
  }
}

/*
* 获取数据
* chart:图表
*/
void calcChartIndicator(FCChart chart) {
  clearDataArr(chart);
  List<double> closeArr = <double>[];
  List<double> highArr = <double>[];
  List<double> lowArr = <double>[];
  if (chart.data.isNotEmpty) {
    for (int i = 0; i < chart.data.length; i++) {
      closeArr.add(chart.data[i].close);
      highArr.add(chart.data[i].high);
      lowArr.add(chart.data[i].low);
    }
  }
  chart.closeArr = closeArr;
  if (chart.mainIndicator == "BOLL") {
    getBollData(closeArr, 20, chart.bollUp, chart.bollMid, chart.bollDown);
  } else if (chart.mainIndicator == "MA") {
    chart.ma5 = maValue(closeArr, 5);
    chart.ma10 = maValue(closeArr, 10);
    chart.ma20 = maValue(closeArr, 20);
    chart.ma30 = maValue(closeArr, 30);
    chart.ma120 = maValue(closeArr, 120);
    chart.ma250 = maValue(closeArr, 250);
  }
  if (chart.showIndicator == "BIAS") {
    getBIASData(closeArr, 6, 12, 24, chart.bias1, chart.bias2, chart.bias3);
  } else if (chart.showIndicator == "DMA") {
    getDMAData(closeArr, 10, 50, chart.dma1, chart.dma2);
  } else if (chart.showIndicator == "BBI") {
    getBBIData(closeArr, 3, 6, 12, 24, chart.bbi);
  } else if (chart.showIndicator == "RSI") {
    getRSIData(closeArr, 6, 12, 24, chart.rsi1, chart.rsi2, chart.rsi3);
  } else if (chart.showIndicator == "ROC") {
    getRocData(closeArr, 12, 6, chart.roc, chart.rocMa);
  } else if (chart.showIndicator == "TRIX") {
    getTRIXData(closeArr, 9, chart.trix, chart.trixMa);
  } else if (chart.showIndicator == "KDJ") {
    getKDJData(
        highArr, lowArr, closeArr, 9, 3, 3, chart.kdjK, chart.kdjD, chart.kdjJ);
  } else if (chart.showIndicator == "WR") {
    getWRData(highArr, lowArr, closeArr, 5, 10, chart.wr1, chart.wr2);
  } else if (chart.showIndicator == "CCI") {
    getCCIData(highArr, lowArr, closeArr, 14, chart.cci);
  } else if (chart.showIndicator == "MACD") {
    //缓存MACD数据
    chart.allema12.add(closeArr[0]);
    chart.allema26.add(closeArr[0]);
    chart.alldeaarr.add(0);
    for (int i = 1; i < closeArr.length; i++) {
      chart.allema12.add(getEMA(12, closeArr[i], chart.allema12[i - 1]));
      chart.allema26.add(getEMA(26, closeArr[i], chart.allema26[i - 1]));
    }
    chart.alldifarr = getDIF(chart.allema12, chart.allema26);
    for (int i = 1; i < chart.alldifarr.length; i++) {
      chart.alldeaarr
          .add(chart.alldeaarr[i - 1] * 8 / 10 + chart.alldifarr[i] * 2 / 10);
    }
    chart.allmacdarr = getMACD(chart.alldifarr, chart.alldeaarr);
  }
  if (chart.getPaint().hasCalculateChartMaxMinEvent) {
    chart.getPaint().onCalculateChartMaxMin(chart);
  } else {
    calculateChartMaxMin(chart);
  }
}

/*
* 计算EMA
* n:周期
* value:当前数据
* lastEMA:上期数据
*/
double getEMA(int n, double value, double lastEMA) {
  return (value * 2 + lastEMA * (n - 1)) / (n + 1);
}

/*
* 计算MACD
* dif:DIF数据
* dea:DEA数据
*/
List<double> getMACD(List<double> dif, List<double> dea) {
  List<double> result = <double>[];
  for (int i = 0; i < dif.length; i++) {
    result.add((dif[i] - dea[i]) * 2);
  }
  return result;
}

/*
* 计算DIF
* close12:12日数据
* close26:26日数据
*/
List<double> getDIF(List<double> close12, List<double> close26) {
  List<double> result = <double>[];
  for (int i = 0; i < close12.length; i++) {
    result.add(close12[i] - close26[i]);
  }
  return result;
}

/*
* 清除缓存数据方法
* chart:图表
*/
void clearDataArr(FCChart chart) {
  chart.allema12 = <double>[];
  chart.allema26 = <double>[];
  chart.alldifarr = <double>[];
  chart.alldeaarr = <double>[];
  chart.allmacdarr = <double>[];
  chart.bollUp = <double>[];
  chart.bollDown = <double>[];
  chart.bollMid = <double>[];
  chart.bias1 = <double>[];
  chart.bias2 = <double>[];
  chart.bias3 = <double>[];
  chart.kdjK = <double>[];
  chart.kdjD = <double>[];
  chart.kdjJ = <double>[];
  chart.rsi1 = <double>[];
  chart.rsi2 = <double>[];
  chart.rsi3 = <double>[];
  chart.roc = <double>[];
  chart.rocMa = <double>[];
  chart.wr1 = <double>[];
  chart.wr2 = <double>[];
  chart.cci = <double>[];
  chart.bbi = <double>[];
  chart.trix = <double>[];
  chart.trixMa = <double>[];
  chart.dma1 = <double>[];
  chart.dma2 = <double>[];
  chart.ma5 = <double>[];
  chart.ma10 = <double>[];
  chart.ma20 = <double>[];
  chart.ma30 = <double>[];
  chart.ma120 = <double>[];
  chart.ma250 = <double>[];
}

/*
* 判断是否选中线条
* chart:图表
* mp:坐标
* divIndex:层索引
* datas:数据
* curIndex:当前索引
*/
bool selectLines(
    FCChart chart, FCPoint mp, int divIndex, List<double> datas, int curIndex) {
  if (datas.isNotEmpty) {
    double topY = getChartY(chart, divIndex, datas[curIndex]);
    if (chart.hScalePixel <= 1) {
      if (mp.y >= topY - 8 && mp.y <= topY + 8) {
        return true;
      }
    } else {
      int index = curIndex;
      double scaleX = getChartX(chart, index);
      double judgeTop = 0;
      double judgeScaleX = scaleX;
      if (mp.x >= scaleX) {
        int leftIndex = curIndex + 1;
        if (curIndex < chart.lastVisibleIndex) {
          double rightValue = datas[leftIndex];
          judgeTop = getChartY(chart, divIndex, rightValue);
        } else {
          judgeTop = topY;
        }
      } else {
        judgeScaleX = scaleX - chart.hScalePixel;
        int rightIndex = curIndex - 1;
        if (curIndex > 0) {
          double leftValue = datas[rightIndex];
          judgeTop = getChartY(chart, divIndex, leftValue);
        } else {
          judgeTop = topY;
        }
      }
      double lineWidth = 4;
      double judgeX = 0, judgeY = 0, judgeW = 0, judgeH = 0;
      if (judgeTop >= topY) {
        judgeX = judgeScaleX;
        judgeY = topY - 2 - lineWidth;
        judgeW = chart.hScalePixel;
        judgeH = judgeTop - topY + lineWidth < 4
            ? 4
            : judgeTop - topY + 4 + lineWidth;
      } else {
        judgeX = judgeScaleX;
        judgeY = judgeTop - 2 - lineWidth / 2;
        judgeW = chart.hScalePixel;
        judgeH = topY - judgeTop + lineWidth < 4
            ? 4
            : topY - judgeTop + 4 + lineWidth;
      }

      if (mp.x >= judgeX &&
          mp.x <= judgeX + judgeW &&
          mp.y >= judgeY &&
          mp.y <= judgeY + judgeH) {
        return true;
      }
    }
  }
  return false;
}

/*
* 判断是否在右轴选中线条
* chart:图表
* mp:坐标
* divIndex:层索引
* datas:数据
* curIndex:当前索引
*/
bool selectLinesInRight(
    FCChart chart, FCPoint mp, int divIndex, List<double> datas, int curIndex) {
  if (datas.isNotEmpty) {
    double topY = getChartYInRight(chart, divIndex, datas[curIndex]);
    if (chart.hScalePixel <= 1) {
      if (mp.y >= topY - 8 && mp.y <= topY + 8) {
        return true;
      }
    } else {
      int index = curIndex;
      double scaleX = getChartX(chart, index);
      double judgeTop = 0;
      double judgeScaleX = scaleX;
      if (mp.x >= scaleX) {
        int leftIndex = curIndex + 1;
        if (curIndex < chart.lastVisibleIndex) {
          double rightValue = datas[leftIndex];
          judgeTop = getChartYInRight(chart, divIndex, rightValue);
        } else {
          judgeTop = topY;
        }
      } else {
        judgeScaleX = scaleX - chart.hScalePixel;
        int rightIndex = curIndex - 1;
        if (curIndex > 0) {
          double leftValue = datas[rightIndex];
          judgeTop = getChartYInRight(chart, divIndex, leftValue);
        } else {
          judgeTop = topY;
        }
      }
      double lineWidth = 4;
      double judgeX = 0, judgeY = 0, judgeW = 0, judgeH = 0;
      if (judgeTop >= topY) {
        judgeX = judgeScaleX;
        judgeY = topY - 2 - lineWidth;
        judgeW = chart.hScalePixel;
        judgeH = judgeTop - topY + lineWidth < 4
            ? 4
            : judgeTop - topY + 4 + lineWidth;
      } else {
        judgeX = judgeScaleX;
        judgeY = judgeTop - 2 - lineWidth / 2;
        judgeW = chart.hScalePixel;
        judgeH = topY - judgeTop + lineWidth < 4
            ? 4
            : topY - judgeTop + 4 + lineWidth;
      }

      if (mp.x >= judgeX &&
          mp.x <= judgeX + judgeW &&
          mp.y >= judgeY &&
          mp.y <= judgeY + judgeH) {
        return true;
      }
    }
  }
  return false;
}

/*
* 判断是否选中图形
* chart:图表
* mp:坐标
*/
void selectShape(FCChart chart, FCPoint mp) {
  chart.selectShape = "";
  chart.selectShapeEx = "";
  double candleHeight = getCandleDivHeight(chart);
  double volHeight = getVolDivHeight(chart);
  double indHeight = getIndDivHeight(chart);
  int index = getChartIndex(chart, mp);
  if (mp.y >= candleHeight + volHeight &&
      mp.y <= candleHeight + volHeight + indHeight) {
    if (chart.showIndicator == "MACD") {
      if (selectLines(chart, mp, 2, chart.allmacdarr, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "MACD";
      }
      if (selectLines(chart, mp, 2, chart.alldifarr, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "DIF";
      } else if (selectLines(chart, mp, 2, chart.alldeaarr, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "DEA";
      }
    } else if (chart.showIndicator == "KDJ") {
      if (selectLines(chart, mp, 2, chart.kdjK, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "K";
      } else if (selectLines(chart, mp, 2, chart.kdjD, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "D";
      } else if (selectLines(chart, mp, 2, chart.kdjJ, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "J";
      }
    } else if (chart.showIndicator == "RSI") {
      if (selectLines(chart, mp, 2, chart.rsi1, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "6";
      } else if (selectLines(chart, mp, 2, chart.rsi2, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "12";
      } else if (selectLines(chart, mp, 2, chart.rsi3, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "24";
      }
    } else if (chart.showIndicator == "BIAS") {
      if (selectLines(chart, mp, 2, chart.bias1, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "1";
      } else if (selectLines(chart, mp, 2, chart.bias2, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "2";
      } else if (selectLines(chart, mp, 2, chart.bias3, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "3";
      }
    } else if (chart.showIndicator == "ROC") {
      if (selectLines(chart, mp, 2, chart.roc, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "ROC";
      } else if (selectLines(chart, mp, 2, chart.rocMa, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "ROCMA";
      }
    } else if (chart.showIndicator == "WR") {
      if (selectLines(chart, mp, 2, chart.wr1, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "1";
      } else if (selectLines(chart, mp, 2, chart.wr2, index)) {
        chart.selectShape = "WR";
        chart.selectShapeEx = "2";
      }
    } else if (chart.showIndicator == "CCI") {
      if (selectLines(chart, mp, 2, chart.cci, index)) {
        chart.selectShape = chart.showIndicator;
      }
    } else if (chart.showIndicator == "BBI") {
      if (selectLines(chart, mp, 2, chart.bbi, index)) {
        chart.selectShape = chart.showIndicator;
      }
    } else if (chart.showIndicator == "TRIX") {
      if (selectLines(chart, mp, 2, chart.trix, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "TRIX";
      } else if (selectLines(chart, mp, 2, chart.trixMa, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "TRIXMA";
      }
    } else if (chart.showIndicator == "DMA") {
      if (selectLines(chart, mp, 2, chart.dma1, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "DIF";
      } else if (selectLines(chart, mp, 2, chart.dma2, index)) {
        chart.selectShape = chart.showIndicator;
        chart.selectShapeEx = "DIFMA";
      }
    }
  } else if (mp.y >= candleHeight && mp.y <= candleHeight + volHeight) {
    double volY = getChartY(chart, 1, chart.data[index].volume);
    double zeroY = getChartY(chart, 1, 0);
    if (mp.y >= min(volY, zeroY) && mp.y <= max(volY, zeroY)) {
      chart.selectShape = "VOL";
    }
  } else if (mp.y >= 0 && mp.y <= candleHeight) {
    bool isTrend = chart.cycle == "trend";
    if (!isTrend) {
      if (chart.mainIndicator == "BOLL") {
        if (selectLines(chart, mp, 0, chart.bollMid, index)) {
          chart.selectShape = chart.mainIndicator;
          chart.selectShapeEx = "MID";
        } else if (selectLines(chart, mp, 0, chart.bollUp, index)) {
          chart.selectShape = chart.mainIndicator;
          chart.selectShapeEx = "UP";
        } else if (selectLines(chart, mp, 0, chart.bollDown, index)) {
          chart.selectShape = chart.mainIndicator;
          chart.selectShapeEx = "DOWN";
        }
      } else if (chart.mainIndicator == "MA") {
        if (selectLines(chart, mp, 0, chart.ma5, index)) {
          chart.selectShape = chart.mainIndicator;
          chart.selectShapeEx = "5";
        } else if (selectLines(chart, mp, 0, chart.ma10, index)) {
          chart.selectShape = chart.mainIndicator;
          chart.selectShapeEx = "10";
        } else if (selectLines(chart, mp, 0, chart.ma20, index)) {
          chart.selectShape = chart.mainIndicator;
          chart.selectShapeEx = "20";
        } else if (selectLines(chart, mp, 0, chart.ma30, index)) {
          chart.selectShape = chart.mainIndicator;
          chart.selectShapeEx = "30";
        } else if (selectLines(chart, mp, 0, chart.ma120, index)) {
          chart.selectShape = chart.mainIndicator;
          chart.selectShapeEx = "120";
        } else if (selectLines(chart, mp, 0, chart.ma250, index)) {
          chart.selectShape = chart.mainIndicator;
          chart.selectShapeEx = "250";
        }
      }
    }
    if (chart.selectShape == "") {
      double highY = getChartY(chart, 0, chart.data[index].high);
      double lowY = getChartY(chart, 0, chart.data[index].low);
      if (isTrend) {
        if (selectLines(chart, mp, 0, chart.closeArr, index)) {
          chart.selectShape = "CANDLE";
        }
      } else {
        if (mp.y >= min(lowY, highY) && mp.y <= max(lowY, highY)) {
          chart.selectShape = "CANDLE";
        }
      }
    }
  }
  if (chart.shapes.isNotEmpty) {
    for (int i = 0; i < chart.shapes.length; i++) {
      BaseShape shape = chart.shapes[i];
      if (shape.leftOrRight) {
        if (selectLines(chart, mp, shape.divIndex, shape.datas, index)) {
          chart.selectShape = shape.shapeName;
          break;
        }
      } else {
        if (selectLinesInRight(chart, mp, shape.divIndex, shape.datas, index)) {
          chart.selectShape = shape.shapeName;
          break;
        }
      }
    }
  }
}

/*
* 触摸开始方法
* view:视图
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
*/
void onTouchBeginDefault(FCView view, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint) {
  if (view.viewType == "tree") {
    touchDownTree(
        view as FCTree, firstTouch, firstPoint, secondTouch, secondPoint, 1);
  } else if (view.viewType == "grid") {
    touchDownGrid(
        view as FCGrid, firstTouch, firstPoint, secondTouch, secondPoint, 1);
  } else if (view.viewType == "div" ||
      view.viewType == "layout" ||
      view.viewType == "menu") {
    touchDownDiv(
        view as FCDiv, firstTouch, firstPoint, secondTouch, secondPoint, 1);
  } else if (view.viewType == "calendar") {
    clickCalendar(view as FCCalendar, firstPoint);
  } else if (view.viewType == "button") {
    invalidateView(view);
  }else if (view.viewType == "chart") {
    touchDownChart(
        view as FCChart, firstTouch, firstPoint, secondTouch, secondPoint);
  } 
}

/*
* 触摸移动方法
* view:视图
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
*/
void onTouchMoveDefault(FCView view, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint) {
  if (view.viewType == "tree") {
    touchMoveTree(
        view as FCTree, firstTouch, firstPoint, secondTouch, secondPoint);
    invalidateView(view);
  } else if (view.viewType == "grid") {
    touchMoveGrid(
        view as FCGrid, firstTouch, firstPoint, secondTouch, secondPoint);
    invalidateView(view);
  } else if (view.viewType == "div" ||
      view.viewType == "layout" ||
      view.viewType == "menu") {
    touchMoveDiv(
        view as FCDiv, firstTouch, firstPoint, secondTouch, secondPoint);
    invalidateView(view);
  } else if (view.viewType == "chart") {
    touchMoveChart(
        view as FCChart, firstTouch, firstPoint, secondTouch, secondPoint);
    invalidateView(view);
  } else if (view.viewType == "menuitem") {
    touchMoveMenuItem(view as FCMenuItem);
  }else{
    invalidateView(view);
  }
}

/*
* 触摸结束方法
* view:视图
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
*/
void onTouchEndDefault(FCView view, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint) {
  if (view.viewType == "tree") {
    touchUpTree(
        view as FCTree, firstTouch, firstPoint, secondTouch, secondPoint, 1);
  } else if (view.viewType == "grid") {
    touchUpGrid(
        view as FCGrid, firstTouch, firstPoint, secondTouch, secondPoint, 1);
  } else if (view.viewType == "div" ||
      view.viewType == "layout" ||
      view.viewType == "menu") {
    touchUpDiv(view as FCDiv, firstTouch, firstPoint, secondTouch, secondPoint, 1);
  } else if (view.viewType == "chart") {
    FCChart chart = view as FCChart;
    chart.firstTouchIndexCacheChart = -1;
    chart.secondTouchIndexCacheChart = -1;
  }
  invalidateView(view);
}

/*
* 鼠标点击实现方法
* view:视图
* firstTouch:是否第一次触摸
* firstPoint:第一次触摸的坐标
* secondTouch:是否第二次触摸
* secondPoint:第二次触摸的坐标
* clicks 点击次数
*/
void onClickDefault(
    FCView view, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint, int clicks) {
  if (view.viewType == "tabbutton") {
    FCTabView tabView = view.getParent() as FCTabView;
    for (int i = 0; i < tabView.tabPages.length; i++) {
      if (tabView.tabPages[i].getHeaderButton() == view) {
        selectTabPage(tabView, tabView.tabPages[i]);
        break;
      }
    }
    invalidateView(tabView);
  } else if (view.viewType == "radiobutton") {
    clickRadioButton(view as FCRadioButton, firstPoint);
    invalidate(view.getPaint());
  } else if (view.viewType == "checkbox") {
    clickCheckBox(view as FCCheckBox, firstPoint);
    invalidateView(view.getParent());
  } else if (view.viewType == "menuitem") {
    clickMenuItem(view as FCMenuItem);
  } else if (view.viewType == "combobox") {
    clickComboBox(view as FCComboBox);
  }
}

/*
* 重绘背景的实现方法
* view:视图
* paint:绘图对象
* clipRect:裁剪区域
*/
void onPaintDefault(FCView view, FCPaint paint, FCRect clipRect) {
  if (view.viewType == "chart") {
    drawChart(view as FCChart, paint, clipRect);
  } else if (view.viewType == "div" ||
      view.viewType == "layout" ||
      view.viewType == "tabpage" ||
      view.viewType == "menu") {
    drawDiv(view, paint, clipRect);
  } else if (view.viewType == "grid") {
    drawDiv(view, paint, clipRect);
    drawGrid(view as FCGrid, paint, clipRect);
  } else if (view.viewType == "tree") {
    drawDiv(view, paint, clipRect);
    drawTree(view as FCTree, paint, clipRect);
  } else if (view.viewType == "calendar") {
    drawCalendar(view as FCCalendar, paint);
  } else if (view.viewType == "label") {
    if (view.textColor != "none") {
      FCSize tSize = paint.textSize(view.text, view.font);
      paint.drawText(view.text, view.textColor, view.font, 0,
          (view.size.cy - tSize.cy) / 2);
    }
  } else if (view.viewType == "textbox") {
    drawDiv(view, paint, clipRect);
    if (view.textColor != "none") {
      FCSize tSize = paint.textSize(view.text, view.font);
      paint.drawText(view.text, view.textColor, view.font, 0,
          (view.size.cy - tSize.cy) / 2);
    }
  } else if (view.viewType == "radiobutton") {
    drawRadioButton(view as FCRadioButton, paint, clipRect);
  } else if (view.viewType == "checkbox") {
    drawCheckBox(view as FCCheckBox, paint, clipRect);
  } else if (view.viewType == "menuitem") {
    drawMenuItem(view as FCMenuItem, paint, clipRect);
  } else if (view.viewType == "combobox") {
    drawComboBox(view as FCComboBox, paint, clipRect);
  } else {
    drawButton(view, paint, clipRect);
  }
}

int updateScrollCount = 0;

/*
* 更新滚动状态
* views:视图集合
*/
void updateScroll(List<FCView> views) {
  int viewsSize = views.length;
  for (int i = 0; i < viewsSize; i++) {
    FCView view = views[i];
    if (view is FCDiv) {
      if (view.isScrolling) {
        checkDivScroll(view);
        updateScrollCount++;
      }
    } else if (view is FCGrid) {
      if (view.isScrolling) {
        checkGridScroll(view);
        updateScrollCount++;
      }
    } else if (view is FCTree) {
      if (view.isScrolling) {
        checkTreeScroll(view);
        updateScrollCount++;
      }
    }
    updateScroll(view.views);
  }
}

/*
* 重绘边框的实现方法
* view:视图
* paint:绘图对象
* clipRect:裁剪区域
*/
void onPaintBorderDefault(FCView view, FCPaint paint, FCRect clipRect) {
  if (view.viewType == "grid") {
    drawGridScrollBar(view as FCGrid, paint, clipRect);
    drawDivBorder(view, paint, clipRect);
  } else if (view.viewType == "tree") {
    drawTreeScrollBar(view as FCTree, paint, clipRect);
    drawDivBorder(view, paint, clipRect);
  } else if (view.viewType == "div" || view.viewType == "layout") {
    drawDivScrollBar(view as FCDiv, paint, clipRect);
    drawDivBorder(view, paint, clipRect);
  } else if (view.viewType == "tabpage") {
    drawDivBorder(view, paint, clipRect);
  } else if (view.viewType == "tabview") {
    drawTabViewBorder(view as FCTabView, paint, clipRect);
  } else if (view.viewType == "textbox") {
    drawDivBorder(view, paint, clipRect);
  }
}

/*
* 设置属性
* view:视图
* node:xml节点
*/
void setAttributeDefault(FCView view, xml.XmlElement node) {
  if (view.getPaint().defaultUIStyle == "dark") {
    view.backColor = "rgb(0,0,0)";
    view.borderColor = "rgb(100,100,100)";
    view.textColor = "rgb(255,255,255)";
    view.scrollBarColor = "rgb(100,100,100)";
  } else if (view.getPaint().defaultUIStyle == "light") {
    view.backColor = "rgb(255,255,255)";
    view.borderColor = "rgb(150,150,150)";
    view.textColor = "rgb(0,0,0)";
    view.scrollBarColor = "rgb(200,200,200)";
  }
  for(xml.XmlAttribute attr in node.attributes){
    String name = attr.name.toString();
    String value = attr.value;
    if (name == "location") {
      List<String> strs = value.split(",");
      String xStr = strs[0];
      String yStr = strs[1];
      if (xStr.contains("%")) {
        view.exAttributes["leftstr"] = xStr;
      } else {
        view.location.x = double.parse(xStr);
      }
      if (yStr.contains("%")) {
        view.exAttributes["topstr"] = yStr;
      } else {
        view.location.y = double.parse(yStr);
      }
    } else if (name == "size") {
      List<String> strs = value.split(",");
      String xStr = strs[0];
      String yStr = strs[1];
      if (xStr.contains("%")) {
        view.exAttributes["widthstr"] = xStr;
      } else {
        view.size.cx = double.parse(xStr);
      }
      if (yStr.contains("%")) {
        view.exAttributes["heightstr"] = yStr;
      } else {
        view.size.cy = double.parse(yStr);
      }
    } else if (name == "text") {
      view.text = value;
    } else if (name == "backcolor") {
      String lowerStr = value.toLowerCase();
      if (lowerStr.contains("rgb")) {
        view.backColor = value;
      } else {
        view.backColor = "none";
      }
    } else if (name == "bordercolor") {
      String lowerStr = value.toLowerCase();
      if (lowerStr.contains("rgb")) {
        view.borderColor = value;
      } else {
        view.borderColor = "none";
      }
    } else if (name == "textcolor") {
      String lowerStr = value.toLowerCase();
      if (lowerStr.contains("rgb")) {
        view.textColor = value;
      } else {
        view.textColor = "none";
      }
    } else if (name == "layoutstyle") {
      if (view is FCLayoutDiv) {
        view.layoutStyle = value.toLowerCase();
      } else if (view is FCSplitLayoutDiv) {
        view.layoutStyle = value.toLowerCase();
      }
    } else if (name == "align") {
      view.align = value.toLowerCase();
    } else if (name == "vertical-align") {
      view.verticalAlign = value.toLowerCase();
    } else if (name == "dock") {
      view.dock = value.toLowerCase();
    } else if (name == "font") {
      view.font = value;
    } else if (name == "tabindex") {
      view.tabIndex = int.parse(value);
    }else if (name == "tabstop") {
      view.tabStop = value.toLowerCase() == "true";
    }else if (name == "headerheight") {
      if (view is FCTree) {
        view.headerHeight = double.parse(value);
      } else if (view is FCGrid) {
        view.headerHeight = double.parse(value);
      }
    }else if (name == "cornerradius") {
      view.cornerRadius = double.parse(value);
    }else if (name == "borderwidth") {
      view.borderWidth = double.parse(value);
    } else if (name == "splitmode") {
      if (view is FCSplitLayoutDiv) {
        view.splitMode = value.toLowerCase();
      }
    } else if (name == "autowrap") {
      if (view is FCLayoutDiv) {
        view.autoWrap = value.toLowerCase() == "true";
      }
    } else if (name == "name") {
      view.viewName = value;
    } else if (name == "enabled") {
      view.enabled = value.toLowerCase() == "true";
    } else if (name == "showvscrollbar") {
      view.showVScrollBar = value.toLowerCase() == "true";
    } else if (name == "showhscrollbar") {
      view.showHScrollBar = value.toLowerCase() == "true";
    } else if (name == "visible") {
      view.visible = value.toLowerCase() == "true";
    } else if (name == "displayoffset") {
      view.displayOffset = value.toLowerCase() == "true";
    } else if (name == "checked") {
      if (view is FCCheckBox) {
        view.checked = value.toLowerCase() == "true";
      } else if (view is FCCheckBox) {
        view.checked = value.toLowerCase() == "true";
      }
    } else if (name == "buttonsize") {
      List<String> strs = value.split(",");
      if (view is FCCheckBox) {
        view.buttonSize = FCSize(double.parse(strs[0]), double.parse(strs[1]));
      } else if (view is FCRadioButton) {
        view.buttonSize = FCSize(double.parse(strs[0]), double.parse(strs[1]));
      }
    } else if (name == "topmost") {
      view.topMost = value.toLowerCase() == "true";
    } else if (name == "groupname") {
      if (view is FCRadioButton) {
        view.groupName = value;
      }
    } else if (name == "allowdragscroll") {
      view.allowDragScroll = value.toLowerCase() == "true";
    } else if (name == "allowpreviewsevent") {
      view.allowPreviewsEvent = value.toLowerCase() == "true";
    } else if (name == "allowdrag") {
      view.allowDrag = value.toLowerCase() == "true";
    } else if (name == "allowresize") {
      view.allowResize = value.toLowerCase() == "true";
    } else if (name == "indent") {
      if (view is FCTree) {
        view.indent = double.parse(value);
      }
    } else if (name == "showcheckbox") {
      if (view is FCTree) {
        view.showCheckBox = value.toLowerCase() == "true";
      }
    } else if (name == "padding") {
      List<String> strs = value.split(",");
      view.padding = FCPadding(double.parse(strs[0]), double.parse(strs[1]),
          double.parse(strs[2]), double.parse(strs[3]));
    } else if (name == "margin") {
      List<String> strs = value.split(",");
      view.margin = FCPadding(double.parse(strs[0]), double.parse(strs[1]),
          double.parse(strs[2]), double.parse(strs[3]));
    } else if (name == "hoveredcolor") {
      String lowerStr = value.toLowerCase();
      if (lowerStr.contains("rgb")) {
        view.hoveredColor = value;
      } else {
        view.hoveredColor = "none";
      }
    } else if (name == "pushedcolor") {
      String lowerStr = value.toLowerCase();
      if (lowerStr.contains("rgb")) {
        view.pushedColor = value;
      } else {
        view.pushedColor = "none";
      }
    } else if (name == "layout") {
      if (view is FCTabView) {
        view.layout = value;
      }
    } else if (name == "width") {
      if (value.contains("%")) {
        view.exAttributes["widthstr"] = value;
      } else {
        view.size.cx = double.parse(value);
      }
    } else if (name == "height") {
      if (value.contains("%")) {
        view.exAttributes["heightstr"] = value;
      } else {
        view.size.cy = double.parse(value);
      }
    } else if (name == "top") {
      if (value.contains("%")) {
        view.exAttributes["topstr"] = value;
      } else {
        view.location.y = double.parse(value);
      }
    } else if (name == "left") {
      if (value.contains("%")) {
        view.exAttributes["leftstr"] = value;
      } else {
        view.location.x = double.parse(value);
      }
    }else{
      view.exAttributes[name] = value;
    }
  }
}

/*
* 读取Xml中的树节点
* tree 树
* parentNode 父节点
* xmlNode Xml节点
*/
void readTreeXmlNodeDefault(FCTree tree, FCTreeNode parentNode, xml.XmlElement xmlNode) {
	FCTreeNode treeNode = FCTreeNode();
  for(xml.XmlAttribute attr in xmlNode.attributes){
    String name = attr.name.toString();
    if(name == "text"){
      treeNode.value = attr.value;
      break;
    }
  }
	appendTreeNode(tree, treeNode, parentNode);
	for(xml.XmlElement child in xmlNode.childElements){
    String nodeName = child.name.toString();
    if (nodeName == "node") {
      readTreeXmlNodeDefault(tree, treeNode, child);
    }
	}
}

/*
* 读取Xml
* paint 绘图对象
* node节点
* parent 父视图
*/
void readXmlNodeDefault(FCPaint paint, xml.XmlElement node, FCView parent) {
  for(xml.XmlElement child in node.childElements){
    String nName = child.name.toString();
    FCView view = FCView();
    view.isNotEmpty = false;
    String nodeName = nName.toLowerCase();
    String type = "";
    if (nodeName == "div" || nodeName == "view") {
      for(xml.XmlAttribute attr in child.attributes){
        String name = attr.name.toString();
        if(name == "type"){
          type = attr.value;
          break;
        }
      }
      if (type == "splitlayout") {
        view = FCSplitLayoutDiv();
      } else if (type == "layout") {
        view = FCLayoutDiv();
      } else if (type == "tab") {
        view = FCTabView();
      } else if (type == "tabpage") {
        view = FCTabPage();
      } else if (type == "radio") {
        view = FCRadioButton();
      } else if (type == "checkbox") {
        view = FCCheckBox();
      } else if (type == "text" || type == "range" || type == "datetime") {
        view = FCTextBox();
      } else if (type == "custom") {
        String cid = "";
        for(xml.XmlAttribute attr in child.attributes){
          String name = attr.name.toString();
          if(name == "cid"){
            type = attr.value;
            break;
          }
        }
        view = FCDiv();
        view.viewType = cid;
      } else {
        view = FCDiv();
      }
    } else if (nodeName == "select") {
      view = FCComboBox();
    } else if (nodeName == "input") {
      for(xml.XmlAttribute attr in child.attributes){
        String name = attr.name.toString();
        if(name == "type"){
          type = attr.value;
          break;
        }
      }
      if (type == "radio") {
        view = FCRadioButton();
      } else if (type == "checkbox") {
        view = FCCheckBox();
      } else if (type == "text" || type == "range" || type == "datetime") {
        view = FCTextBox();
      } else if (type == "custom") {
        String cid = "";
        for(xml.XmlAttribute attr in child.attributes){
          String name = attr.name.toString();
          if(name == "cid"){
            type = attr.value;
            break;
          }
        }
        view = FCView();
        view.viewType = cid;
      } else {
        view = FCButton();
      }
    } else if (nodeName == "chart") {
      view = FCChart();
    } else if (nodeName == "calendar") {
      view = FCCalendar();
    } else if (nodeName == "table") {
      view = FCGrid();
    } else if (nodeName == "tree") {
      view = FCTree();
    } else if (nodeName == "label") {
      view = FCLabel();
    } else {
      view = FCView();
    }
    view.setPaint(paint);
    if (parent.isNotEmpty) {
      view.setParent(parent);
    }
    setAttributeDefault(view, child);
    if (view.isNotEmpty) {
      if (type == "tabpage") {
        FCButton tabButton = FCButton();
        tabButton.viewType = "tabbutton";
        String atrHeaderSize = "";
        for(xml.XmlAttribute attr in child.attributes){
          String name = attr.name.toString();
          if(name == "headersize"){
            atrHeaderSize = attr.value;
            break;
          }
        }
        if (atrHeaderSize.isNotEmpty) {
          List<String> strs = atrHeaderSize.split(",");
          tabButton.size = FCSize(double.parse(strs[0]), double.parse(strs[1]));
        } else {
          tabButton.size = FCSize(100, 20);
        }
        if (view.getPaint().defaultUIStyle == "dark") {
          tabButton.backColor = "rgb(0,0,0)";
          tabButton.borderColor = "rgb(100,100,100)";
          tabButton.textColor = "rgb(255,255,255)";
        } else if (view.getPaint().defaultUIStyle == "light") {
          tabButton.backColor = "rgb(255,255,255)";
          tabButton.borderColor = "rgb(150,150,150)";
          tabButton.textColor = "rgb(0,0,0)";
        }
        tabButton.text = view.text;
        addTabPage(view.getParent() as FCTabView, view as FCTabPage, tabButton);
      } else {
        if (parent.isNotEmpty) {
          parent.views.add(view);
        } else {
          paint.views.add(view);
        }
      }
    }
    if (type == "splitlayout") {
      String atrDatum = "";
      String canDragSplitter = "";
      String splitterposition = "";
      for(xml.XmlAttribute attr in child.attributes){
        String name = attr.name.toString();
        if(name == "datumsize"){
          atrDatum = attr.value;
        }else if(name == "candragsplitter"){
          canDragSplitter = attr.value;
        }else if(name == "splitterposition"){
          splitterposition = attr.value;
        }
      }
      if (atrDatum.isNotEmpty) {
        List<String> strs = atrDatum.split(",");
        view.size = FCSize(double.parse(strs[0]), double.parse(strs[1]));
      }
      FCView splitter = FCView();
      splitter.setParent(view);
      if (view.getPaint().defaultUIStyle == "dark") {
        splitter.backColor = "rgb(100,100,100)";
      } else if (view.getPaint().defaultUIStyle == "light") {
        splitter.backColor = "rgb(150,150,150)";
      }
      if (canDragSplitter.isNotEmpty) {
        if (canDragSplitter == "true") {
          splitter.allowDrag = true;
        }
      }
      splitter.setPaint(paint);
      view.views.add(splitter);
      FCSplitLayoutDiv splitLayoutDiv = view as FCSplitLayoutDiv;
      splitLayoutDiv.setSplitter(splitter);
      List<String> splitStr = splitterposition.split(",");
      if (splitStr.length >= 4) {
        FCRect splitRect = FCRect(
            double.parse(splitStr[0]),
            double.parse(splitStr[1]),
            double.parse(splitStr[2]),
            double.parse(splitStr[3]));
        splitter.location = FCPoint(splitRect.left, splitRect.top);
        splitter.size = FCSize(
            splitRect.right - splitRect.left, splitRect.bottom - splitRect.top);
      } else {
        double sSize = double.parse(splitStr[1]);
        double sPosition = double.parse(splitStr[0]);
        if (splitLayoutDiv.layoutStyle == "lefttoright" ||
            splitLayoutDiv.layoutStyle == "righttoleft") {
          splitter.location = FCPoint(sPosition, 0);
          splitter.size = FCSize(sSize, view.size.cy);
        } else {
          splitter.location = FCPoint(0, sPosition);
          splitter.size = FCSize(view.size.cx, sSize);
        }
      }
      readXmlNodeDefault(paint, child, view);
      splitLayoutDiv.setFirstView(view.views[1]);
      splitLayoutDiv.setSecondView(view.views[2]);
      splitLayoutDiv.oldSize = FCSize(view.size.cx, view.size.cy);
      resetSplitLayoutDiv(splitLayoutDiv);
    } else if (type == "tab") {
      FCTabView tabView = view as FCTabView;
      readXmlNodeDefault(paint, child, view);
      String strSelectedIndex = "";
      for(xml.XmlAttribute attr in child.attributes){
        String name = attr.name.toString();
        if(name == "selectedindex"){
          strSelectedIndex = attr.value;
          break;
        }
      }
      if (tabView.tabPages.isNotEmpty) {
        if (strSelectedIndex.isNotEmpty) {
          int selectedIndex = int.parse(strSelectedIndex);
          if (selectedIndex >= 0 && selectedIndex < tabView.tabPages.length) {
            tabView.tabPages[selectedIndex].visible = true;
          } else {
            tabView.tabPages[tabView.tabPages.length - 1].visible = true;
          }
        } else {
          tabView.tabPages[tabView.tabPages.length - 1].visible = true;
        }
      }
    }else if (nodeName == "table") {
      for(xml.XmlElement tChild in child.childElements){
        if (tChild.name.toString() == "tr") {
          FCGridRow gridRow = FCGridRow();
          gridRow.isNotEmpty = false;
          for(xml.XmlElement tSubChild in tChild.childElements){
            String sunNodeName = tSubChild.name.toString().toLowerCase();
            if (sunNodeName == "th") {
              FCGridColumn gridColumn = FCGridColumn();
              if (view.getPaint().defaultUIStyle == "light") {
                gridColumn.backColor = "rgb(230,230,230)";
                gridColumn.borderColor = "rgb(150,150,150)";
                gridColumn.textColor = "rgb(0,0,0)";
              } else if (view.getPaint().defaultUIStyle == "dark") {
                gridColumn.backColor = "rgb(50,50,50)";
                gridColumn.borderColor = "rgb(100,100,100)";
                gridColumn.textColor = "rgb(255,255,255)";
              }
              gridColumn.width = 100;
              for(xml.XmlAttribute attr in tSubChild.attributes){
                String name = attr.name.toString();
                if(name == "width"){
                  String widthStr = attr.value;
                  if (widthStr.contains("%")) {
                    gridColumn.widthStr = widthStr;
                  } else {
                    gridColumn.width = double.parse(widthStr);
                  }
                }else if(name == "text"){
                  gridColumn.text = attr.value;
                }else if(name == "backcolor"){
                  gridColumn.backColor = attr.value;
                }else if(name == "textcolor"){
                  gridColumn.textColor = attr.value;
                }else if(name == "bordercolor"){
                  gridColumn.borderColor = attr.value;
                }else if(name == "font"){
                  gridColumn.font = attr.value;
                }
              }
              (view as FCGrid).columns.add(gridColumn);
            }else if (sunNodeName == "td") {
              if (!gridRow.isNotEmpty) {
                  gridRow = FCGridRow();
                  (view as FCGrid).rows.add(gridRow);
              }
              FCGridCell gridCell = FCGridCell();
              gridCell.value = tSubChild.text;
              gridRow.cells.add(gridCell);
            }
          }
        }
      }
    } else if (nodeName == "tree") {
      FCTreeColumn treeColumn = FCTreeColumn();
      (view as FCTree).columns.add(treeColumn);
      double columnWidth = 0.0;
      for(xml.XmlElement tChild in child.childElements){
        if (tChild.name.toString() == "nodes") {
          for(xml.XmlElement tSubChild in tChild.childElements){
            String sunNodeName = tSubChild.name.toString().toLowerCase();
            if (sunNodeName == "node") {
              FCTreeNode noneNode = FCTreeNode();
              noneNode.isNotEmpty = false;
              readTreeXmlNodeDefault(view, noneNode, tSubChild);
            }
          }
        }else if (tChild.name.toString() == "tr") {
          for(xml.XmlElement tSubChild in tChild.childElements){
            String sunNodeName = tSubChild.name.toString().toLowerCase();
            if (sunNodeName == "th") {
              for(xml.XmlAttribute attr in tSubChild.attributes){
                String name = attr.name.toString();
                if(name == "width"){
                  String widthStr = attr.value;
                  if (widthStr.contains("%")) {
                    treeColumn.widthStr = widthStr;
                  } else {
                    columnWidth += double.parse(widthStr);
                  }
                }
              }
            }
          }
        }
      }
      if (columnWidth > 0) {
        treeColumn.width = columnWidth;
      }
    } else if (nodeName == "calendar") {
      FCCalendar calendar = view as FCCalendar;
      initCalendar(calendar);
      CYear cYear = getYear(calendar.years, 2022);
      CMonth cMonth = cYear.months[10] as CMonth;
      CDay cDay = cMonth.days[1] as CDay;
      calendar.setSelectedDay(cDay);
      updateCalendar(calendar);
    } else if (nodeName == "select") {
      FCComboBox comboBox = view as FCComboBox;
      comboBox.setDropDownMenu(FCMenu());
      comboBox.getDropDownMenu().setComboBox(comboBox);
      addView(comboBox.getDropDownMenu(), paint);
      view.getDropDownMenu().size.cx = view.size.cx;
      for(xml.XmlElement tChild in child.childElements){
        if (tChild.name.toString() == "option") {
          FCMenuItem menuItem = FCMenuItem();
          addMenuItem(menuItem, comboBox.getDropDownMenu());
          setAttributeDefault(menuItem, tChild);
        }
      }
      String strSelectedIndex = "";
      for(xml.XmlAttribute attr in child.attributes){
        String name = attr.name.toString();
        if(name == "selectedindex"){
          strSelectedIndex = attr.value;
        }
      }
      if (comboBox.getDropDownMenu().items.isNotEmpty) {
        if (strSelectedIndex.isNotEmpty) {
          int selectedIndex = int.parse(strSelectedIndex);
          if (selectedIndex >= 0 &&
              selectedIndex < comboBox.getDropDownMenu().items.length) {
            comboBox.selectedIndex = selectedIndex;
            comboBox.getDropDownMenu().items[selectedIndex].visible = true;
          } else {
            comboBox.selectedIndex = 0;
            comboBox.getDropDownMenu().items[0].visible = true;
          }
        } else {
          comboBox.selectedIndex = 0;
          comboBox.getDropDownMenu().items[0].visible = true;
        }
      }
    } else {
      if (view.viewType != "chart") {
        readXmlNodeDefault(paint, child, view);
      }
    }
  }
}

/*
* 加载图形界面
* paint:绘图对象
* xmlStr:Xml的内容
* size:尺寸大小
*/
void renderFaceCat(FCPaint paint, String xmlString) {
  xml.XmlDocument document = xml.XmlDocument.parse(xmlString);
  xml.XmlElement root = document.rootElement;
  for(xml.XmlElement subNode in root.childElements){
    if(subNode.name.toString() == "body"){
      FCView noneView = FCView();
      noneView.isNotEmpty = false;
      readXmlNodeDefault(paint, subNode, noneView);
    }
  }
  if (paint.hasUpdateViewEvent) {
    paint.onUpdateView(paint.views);
  } else {
    updateViewDefault(paint.views);
  }
  invalidate(paint);
}

/*
* 重绘视图
*/
void onPaintCanvas(Canvas canvas, Size size, FCPaint paint){
    paint.canvas = canvas;
    if(paint.size.cx != size.width || paint.size.cy != size.height){
        paint.size = FCSize(size.width, size.height);
        updateViewDefault(paint.views);
    }
    List<FCView> drawViews = paint.views;
    paint.beginPaint();
    FCRect drawRect = FCRect(
        0,
        0,
        paint.size.cx / paint.scaleFactorX,
        paint.size.cy / paint.scaleFactorY);
    renderViews(drawViews, paint, drawRect);
    paint.endPaint();
}

/*
 * 触摸开始事件
 * paint 绘图对象
 * firstTouch:是否第一次触摸
 * firstPoint:第一次触摸的坐标
 * secondTouch:是否第二次触摸
 * secondPoint:第二次触摸的坐标
 */
void handleTouchDown(FCPaint paint, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint) {
  paint.cancelClick = false;
  FCPoint mp = firstPoint;
  paint.touchDownPoint = FCPoint(mp.x, mp.y);
  paint.touchDownView = <FCView>[];
  int newClickTime = getTickCount();
  if(newClickTime - paint.lastClickTime < 250){
    paint.isDoubleClick = true;
    paint.lastClickTime = 0;
  }else{
    paint.isDoubleClick = false;
    paint.lastClickTime = newClickTime;
  }	
  FCView fView = findView(mp, paint.views);
  if (fView.isNotEmpty) {
    paint.setTouchDownView(fView);
    checkShowMenu(paint);
    paint.setFocusedView(paint.getTouchDownView());
    double clx = clientX(paint.getTouchDownView());
    double cly = clientY(paint.getTouchDownView());
    firstPoint.x -= clx;
    firstPoint.y -= cly;
    secondPoint.x -= clx;
    secondPoint.y -= cly;
    if (paint.hasTouchBeginEvent) {
      paint.onTouchBegin(paint.getFocusedView(), firstTouch, firstPoint,
          secondTouch, secondPoint);
    } else {
      onTouchBeginDefault(paint.getFocusedView(), firstTouch, firstPoint,
          secondTouch, secondPoint);
    }
    if(fView.allowResize){
      fView.resizePoint = getResizeState(fView, firstPoint);
      if(fView.resizePoint != -1){
        fView.startRect = FCRect(fView.location.x, fView.location.y, fView.location.x + fView.size.cx,
          fView.location.y + fView.size.cy);
      }
    }
  }
}

/*
 * 触摸移动事件
 * paint 绘图对象
 * firstTouch:是否第一次触摸
 * firstPoint:第一次触摸的坐标
 * secondTouch:是否第二次触摸
 * secondPoint:第二次触摸的坐标
 */
void handleTouchMove(FCPaint paint, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint) {
  if (paint.touchDownView.isNotEmpty) {
    FCPoint mp = firstPoint;
    FCView mDownView = paint.getTouchDownView();
    FCView pParent = findPreviewsEventParent(paint.getFocusedView());
    if (pParent.isNotEmpty &&
        pParent.allowDragScroll &&
        (pParent.viewType == "div" ||
            pParent.viewType == "layout" ||
            pParent.viewType == "menu")) {
      paint.setTouchDownView(pParent);
      paint.setFocusedView(pParent);
      paint.setTouchMoveView(pParent);
      double clx = clientX(pParent);
      double cly = clientY(pParent);
      FCPoint cmp = FCPoint(mp.x, mp.y);
      cmp.x -= clx;
      cmp.y -= cly;
      touchDownDiv(
          pParent as FCDiv, firstTouch, cmp, secondTouch, secondPoint, 1);
      touchMoveDiv(
          pParent as FCDiv, firstTouch, cmp, secondTouch, secondPoint);
      invalidateView(pParent);
    } else {
      double clx = clientX(mDownView);
      double cly = clientY(mDownView);
      FCPoint cmp = FCPoint(mp.x, mp.y);
      cmp.x -= clx;
      cmp.y -= cly;
      if (paint.hasTouchMoveEvent) {
        paint.onTouchMove(
            mDownView, firstTouch, cmp, secondTouch, secondPoint);
      } else {
        onTouchMoveDefault(
            mDownView, firstTouch, cmp, secondTouch, secondPoint);
      }
      if(mDownView.resizePoint != -1){
        FCRect newBounds = FCRect(mDownView.startRect.left, mDownView.startRect.top, mDownView.startRect.right, mDownView.startRect.bottom);
        windowResize(newBounds, mDownView.resizePoint, mp, paint.touchDownPoint);
        mDownView.location = FCPoint(newBounds.left, newBounds.top);
        mDownView.size = FCSize(newBounds.right - newBounds.left, newBounds.bottom - newBounds.top);
        if (mDownView.getParent().isNotEmpty) {
          invalidateView(mDownView.getParent());
        } else {
          invalidate(paint);
        }
      }
      else if (mDownView.allowDrag) {
        if ((mp.x - paint.touchDownPoint.x).abs() > 5 ||
            (mp.y - paint.touchDownPoint.y).abs() > 5) {
          paint.dragBeginRect = FCRect(
              mDownView.location.x,
              mDownView.location.y,
              mDownView.location.x + mDownView.size.cx,
              mDownView.location.y + mDownView.size.cy);
          paint.dragBeginPoint =
              FCPoint(paint.touchDownPoint.x, paint.touchDownPoint.y);
          paint.setDraggingView(mDownView);
          paint.touchDownView = <FCView>[];
        }
      }
    }
  } else if (paint.getDraggingView().isNotEmpty) {
    FCPoint mp = firstPoint;
    double offsetX = mp.x - paint.dragBeginPoint.x;
    double offsetY = mp.y - paint.dragBeginPoint.y;
    FCRect newBounds = FCRect(
        paint.dragBeginRect.left + offsetX,
        paint.dragBeginRect.top + offsetY,
        paint.dragBeginRect.right + offsetX,
        paint.dragBeginRect.bottom + offsetY);
    FCView dView = paint.getDraggingView();
    dView.location = FCPoint(newBounds.left, newBounds.top);
    if (paint.getDraggingView().getParent().isNotEmpty && paint.getDraggingView().getParent().viewType == "split") {
		resetSplitLayoutDiv(paint.getDraggingView().getParent() as FCSplitLayoutDiv);
		if (paint.hasUpdateViewEvent) {
            paint.onUpdateView(paint.views);
          } else {
            updateViewDefault(paint.views);
          }
	}
    if (dView.getParent().isNotEmpty) {
      invalidateView(dView.getParent());
    } else {
      invalidate(paint);
    }
  }
}

/*
 * 触摸抬起事件
 * paint 绘图对象
 * firstTouch:是否第一次触摸
 * firstPoint:第一次触摸的坐标
 * secondTouch:是否第二次触摸
 * secondPoint:第二次触摸的坐标
 */
void handleTouchUp(FCPaint paint, bool firstTouch, FCPoint firstPoint,
    bool secondTouch, FCPoint secondPoint) {
  if (paint.touchDownView.isNotEmpty) {
    FCPoint mp = firstPoint;
    FCView mDownView = paint.getTouchDownView();
    double clx = clientX(mDownView);
    double cly = clientY(mDownView);
    FCPoint cmp = FCPoint(mp.x, mp.y);
    cmp.x -= clx;
    cmp.y -= cly;
    int clicks = 1;
      if(paint.isDoubleClick){
        clicks = 2;
      }
    FCView view = findView(mp, paint.views);
    if (view.isNotEmpty && view == mDownView) {
      if (paint.hasClickEvent) {
        paint.onClick(view, true, cmp, false, cmp, clicks);
      } else {
        onClickDefault(view, true, cmp, false, cmp, clicks);
      }
    }
    if (paint.hasTouchEndEvent) {
      paint.onTouchEnd(mDownView, firstTouch, cmp, secondTouch, secondPoint);
    } else {
      onTouchEndDefault(mDownView, firstTouch, cmp, secondTouch, secondPoint);
    }
    mDownView.resizePoint = -1;
    paint.touchDownView = <FCView>[];
  }
  paint.draggingView = <FCView>[];
}

/*
* 执行按下事件
* event: 事件
* paint: 绘图对象
*/
void doPointerDown(PointerDownEvent event, Map<int, Offset> touchPositions, FCPaint paint){
  int pos = 0;
  FCPoint firstPoint = FCPoint(0, 0);
  FCPoint secondPoint = FCPoint(0, 0);
  bool firstTouch = false;
  bool secondTouch = false;
  touchPositions.forEach((key, value) {
    if (pos == 0) {
      firstTouch = true;
      firstPoint = FCPoint(value.dx, value.dy);
      firstPoint.x /= paint.scaleFactorX;
      firstPoint.y /= paint.scaleFactorY;
    } else if (pos == 1) {
      secondTouch = true;
      secondPoint = FCPoint(value.dx, value.dy);
      secondPoint.x /= paint.scaleFactorX;
      secondPoint.y /= paint.scaleFactorY;
    }
    pos++;
  });
  handleTouchDown(paint, firstTouch, firstPoint, secondTouch, secondPoint);
}

/*
* 执行移动事件
* event: 事件
* paint: 绘图对象
*/
void doPointerMove(PointerMoveEvent event, Map<int, Offset> touchPositions, FCPaint paint){
  int pos = 0;
  FCPoint firstPoint = FCPoint(0, 0);
  FCPoint secondPoint = FCPoint(0, 0);
  bool firstTouch = false;
  bool secondTouch = false;
  touchPositions.forEach((key, value) {
    if (pos == 0) {
      firstTouch = true;
      firstPoint = FCPoint(value.dx, value.dy);
      firstPoint.x /= paint.scaleFactorX;
      firstPoint.y /= paint.scaleFactorY;
    } else if (pos == 1) {
      secondTouch = true;
      secondPoint = FCPoint(value.dx, value.dy);
      secondPoint.x /= paint.scaleFactorX;
      secondPoint.y /= paint.scaleFactorY;
    }
    pos++;
  });
  handleTouchMove(paint, firstTouch, firstPoint, secondTouch, secondPoint);
}

/*
* 执行抬起事件
* event: 事件
* paint: 绘图对象
*/
void doPointerUp(PointerUpEvent event, Map<int, Offset> touchPositions, FCPaint paint){
  int pos = 0;
  FCPoint firstPoint = FCPoint(0, 0);
  FCPoint secondPoint = FCPoint(0, 0);
  bool firstTouch = false;
  bool secondTouch = false;
  touchPositions.forEach((key, value) {
    if (pos == 0) {
      firstTouch = true;
      firstPoint = FCPoint(value.dx, value.dy);
      firstPoint.x /= paint.scaleFactorX;
      firstPoint.y /= paint.scaleFactorY;
    } else if (pos == 1) {
      secondTouch = true;
      secondPoint = FCPoint(value.dx, value.dy);
      secondPoint.x /= paint.scaleFactorX;
      secondPoint.y /= paint.scaleFactorY;
    }
    pos++;
  });
  handleTouchUp(paint, firstTouch, firstPoint, secondTouch, secondPoint);
}